Обновление - Многие люди настаивают, чтобы я объявил iVar для свойства. Некоторые говорят не так, поскольку я использую современное Время выполнения (64 бита). Я могу подтвердить, что успешно использовал @property без iVars в течение многих месяцев теперь. Поэтому я думаю, что 'корректный' ответ является объяснением относительно того, почему на 64 битах я внезапно должен явно объявить iVar, когда (и только когда) я собираюсь получить доступ к нему от дочернего класса. Единственный, который я видел до сих пор, является возможной ошибкой GCC (благодарит Yuji). Не настолько простой, в конце концов... Разъяснить возможную ошибку - это: При наследовании базовому классу ребенок не может получить доступ к iVar родителя, ЕСЛИ тот ребенок также, оказывается, реализует НЕСВЯЗАННОЕ средство доступа с помощью @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
Я думаю, что это ошибка 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-битном режиме можно опустить переменную instance. Мой 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.)
что является вполне ожидаемым поведением, как написал Manjunath.)
Однако я думаю, что в целом это довольно плохая идея обращаться к переменной экземпляра суперкласса: когда вы реализуете методы суперкласса, вы не можете ничего предполагать о переменной экземпляра, потому что она может быть изменена подклассом наихудшим образом. Вам, по крайней мере, нужно записать, какие операции над переменной экземпляра разрешены, а какие нет... Помните, что вам, возможно, придется поддерживать ваш код в течение многих лет! Я бы предпочел сохранять программные контракты между различными частями кода на уровне методов и свойств.
Наконец, вы должны изменить
@property NSString *testProp;
на
@property (copy) NSString *testProp;
или, по крайней мере, на
@property (retain) NSString *testProp;
если вы не используете GC в OS X. В противном случае вас будет ждать EXP_BAD_ACCESS!
Думаю, у вас просто опечатка - должно быть "testString", а не "test"
Я вижу ошибку: 'testString' undeclared (первое использование в этой функции)
, когда @synthesize
находится непосредственно перед testing
метода. Ошибка исчезает, если переместить @synthesize
ниже реализации метода. Это может быть связано с тем, что класс TestB не имеет строковой переменной экземпляра testProp
для использования с объявленным свойством. (В Legacy (32-битной) среде выполнения вы должны объявить переменные экземпляра для использования свойств - в Modern среде выполнения (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 код работает без изменений. Возможно, это ошибка, которую нужно устранить.