(Не Так) Глупая проблема наследования Objective C при использовании свойства - Ошибка GCC?

Обновление - Многие люди настаивают, чтобы я объявил 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
13
задан jv42 23 November 2010 в 12:57
поделиться

3 ответа

Я думаю, что это ошибка 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!

10
ответ дан 2 December 2019 в 00:45
поделиться

Думаю, у вас просто опечатка - должно быть "testString", а не "test"

1
ответ дан 2 December 2019 в 00:45
поделиться

Я вижу ошибку: '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 код работает без изменений. Возможно, это ошибка, которую нужно устранить.

1
ответ дан 2 December 2019 в 00:45
поделиться