(Не так) Глупое Objective-C проблема наследования при использовании свойства - ошибка GCC?
Обновление. Многие люди настаивают, что мне нужно объявить iVar для этого свойства. Некоторые говорят не так, поскольку я использую Modern Runtime (64 бит). Я могу подтвердить, что я успешно использовал @property без iVars уже несколько месяцев. Поэтому я считаю, что "правильный" ответ объясняет, почему на 64-битном я вдруг должен явно объявить iVar, когда (и только когда) я собираюсь получить к нему доступ из дочернего класса. Единственный, который я видел до сих пор, - это возможная ошибка GCC (спасибо Юдзи). Не так просто в конце концов... Чтобы прояснить возможную ошибку, выполните следующее: При наследовании от базового класса ребенок не может получить доступ к родительскому iVar IF, если дочерний объект также реализует UNRELATED-аксессуар, используя @synthesize ПЕРЕД тем, к которому обращается iVar.
Я уже несколько часов царапаю голову этим, я не использовал наследование много.
Здесь я установил простой класс теста B, который наследуется от теста A, где объявлен ivar. Но я получаю ошибку компиляции, что переменная не объявлена. Это происходит только тогда, когда я добавляю свойство и синтезирую декларации - отлично работает без них.
Заголовок TestA:
#import <Cocoa/Cocoa.h>
@interface TestA : NSObject {
NSString *testString;
}
@end
TestA Реализация пуста:
#import "TestA.h"
@implementation TestA
@end
Заголовок TestB:
#import <Cocoa/Cocoa.h>
#import "TestA.h"
@interface TestB : TestA {
}
@property (nonatomic, retain) NSString *testProp;
@end
Реализация TestB (ошибка - "testString" не объявлена)
#import "TestB.h"
@implementation TestB
@synthesize testProp;
- (void)testing{
NSLog(@"test ivar is %@", testString);
}
@end
Ответы
Ответ 1
Я думаю, что это ошибка GCC 4.2.1.
Я сделал файл foo.m
с содержимым
#import <Foundation/Foundation.h>
@interface TestA : NSObject {
NSString *testString;
}
@end
@implementation TestA
@end
@interface TestB : TestA {
}
@property (retain) NSString *testProp;
@end
@implementation TestB
@synthesize testProp;
- (void)testing{
NSLog(@"test ivar is %@", testString);
}
@end
Обратите внимание, что в 64-битном режиме это нормально, чтобы опустить переменную экземпляра.
Мой GCC 4.2.1 на OS X 10.6.3 дал мне ошибку:
$ gcc -arch x86_64 -c foo.m
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)
Это скомпилировано без проблем, изменив
NSLog(@"test ivar is %@", testString);
к
NSLog(@"test ivar is %@", self->testString);
Clang скомпилировал его без каких-либо проблем.
(В 32-битном режиме я получил
$ gcc -arch i386 -c foo.m
aho.m:17: error: synthesized property ‘testProp’ must either be named
the same as a compatible ivar or must explicitly name an ivar
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)
который является вполне ожидаемым поведением, как писал Манджунат.)
Однако Я думаю, что в целом довольно плохая идея получить доступ к переменной экземпляра суперкласса: когда вы реализуете методы суперкласса, вы не можете ничего принять за переменную экземпляра, потому что она может быть изменена в наихудшим способом подкласса. Вам, по крайней мере, нужно записать, какая операция над переменной экземпляра разрешена или нет... Помните, что вам может понадобиться поддерживать код в течение многих лет! Я бы предпочел сохранить контракты программирования между различными частями кода на уровне методов и свойств.
Наконец, вы должны изменить
@property NSString *testProp;
к
@property (copy) NSString *testProp;
или, по крайней мере,
@property (retain) NSString *testProp;
если вы не используете GC на OS X. В противном случае EXP_BAD_ACCESS будет ждать вас!
Ответ 2
Я думаю, что у вас просто опечатка - это должно быть "testString", а не "test"
Ответ 3
Я вижу error: 'testString' undeclared (first use in this function)
, когда @synthesize
находится перед методом testing
. Ошибка исчезает, если я перемещаю @synthesize
ниже реализации метода. Возможно, это связано с тем, что класс TestB не имеет переменной экземпляра строки testProp
для использования с объявленным свойством. (В среде исполнения Legacy (32-разрядная версия) вы должны объявлять переменные экземпляра для использования для свойств - в Modern runtime (64-разрядный Mac, iPhone) они могут быть выведены, поэтому объявление их необязательно. ) Возможно ли, что вы хотели вместо этого указать свойство testString
?
EDIT: В GCC 4.2 он работает, если вы измените TestB.h на следующее:
#import "TestA.h"
@interface TestB : TestA {
NSString *testProp; // <-- Adding this fixes the errors
}
@property NSString *testProp;
@end
Однако, используя компилятор Clang-LLVM, код работает без изменений. Возможно, это ошибка для файла.
Ответ 4
Я просто столкнулся с той же проблемой, но источники были достаточно сложными, и я не совсем понял, что сделало iVar от недоступного родителя, когда я использовал GCC. Я только точно знал, что несколько месяцев назад и до изменений в моем коде он работал, а также что он работает с clang, который я использовал некоторое время. Неожиданно мне пришлось строить с GCC, и это больше не будет.
По крайней мере, эта статья дала мне обход (объявить iVars). Я удивлен, что последняя версия XCode не включает фиксированный компилятор