Если подкласс ссылается на суперкласс ivar, синтез несвязанного свойства завершается неудачно - PullRequest
18 голосов
/ 26 июля 2011

Редактировать: Я только что заметил этот другой вопрос переполнения стека, задающий почти то же самое: Почему подкласс @property без соответствующего ivar скрывает суперкласс ivar?

Это интересное поведение, которое я не могу найти ни в одном официальном или неофициальном документе (блог, твит, вопрос SO и т. Д.).Я довел его до сути и протестировал в новом проекте XCode, но не могу объяснить это.

У MyBaseClass есть переменная экземпляра:

@interface MyBaseClass : NSObject {
    NSObject *fooInstanceVar;
}
@end

MySubclass extends MyBaseClass,и объявляет полностью несвязанное свойство (то есть свойство не предназначено для поддержки переменной экземпляра):

#import "MyBaseClass.h"
@interface MySubclass : MyBaseClass { }
@property (nonatomic, retain) NSObject *barProperty;
@end

Если реализация MySubclass не синтезирует свойство, нореализует методы доступа, все нормально (без ошибки компилятора):

#import "MySubclass.h"
@implementation MySubclass

- (NSObject*)barProperty {
    return [[NSObject alloc] init]; // pls ignore flagrant violation of memory rules.
}

- (void)setBarProperty:(NSObject *)obj { /* no-op */ }

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

Но если я удаляю методы доступа к свойству и заменяю их объявлением synthesize для свойства, я получаю ошибку компилятора:'fooInstanceVar' undeclared (first use in this function).

#import "MySubclass.h"
@implementation MySubclass
@synthesize barProperty;

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

Эта ошибка исчезнет, ​​если я удалю либо объявление synthesize, либо не ссылаюсь на переменную экземпляра fooInstanceVar из MySubclass.m, либо если я добавлювсе определения интерфейса и реализации в одном файле.Похоже, эта ошибка также возникает в настройках сборки GCC 4.2 и GCC / LLVM.

Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 3 ]

5 голосов
/ 05 августа 2011

Как ответили на этот вопрос: target c xcode 4.0.2: подкласс не может получить доступ к переменным суперкласса "не был объявлен в этой области"

Из документа: Apple Objective-Язык программирования C: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF125

Переменная экземпляра доступна в классе, который ее объявляет, и в классах, которые ее наследуют.Все переменные экземпляра без явной директивы области действия имеют @protected scope.

Однако к общедоступной переменной экземпляра можно получить доступ где угодно, как если бы это было поле в структуре C.Например:

Worker * ceo = [[Worker alloc] init];

ceo-> boss = nil;

У меня ошибка компиляции с использованием LLVMGCC 4.2 (для проекта iOS на устройстве):

ошибка: 'fooInstanceVar' undeclared (первое использование в этой функции) и та же, с использованием GCC 4.2: ошибка: 'fooInstanceVar' undeclared (перваяиспользуйте в этой функции)

Я могу скомпилировать, используя LLVM Compiler 2.0 без ошибки.

Для компиляции с LLVM GCC 4.2 и GCC 4.2 с использованием self->:

[NSArray arrayWithObjects: self.barProperty, self-> fooInstanceVar, nil];в методе doSomethingWithProperty.

0 голосов
/ 04 августа 2011

Я тоже не могу воспроизвести вашу ошибку. У вас установлен флаг компилятора не по умолчанию? Не могли бы вы предоставить копию своего проекта? Это определенно является ошибкой в ​​компиляторе.

Прочтите эту статью здесь для лучшего использования @ property / @ synthesize. Краткий обзор - удалить все ваши ivars из ваших объектов (если вам по какой-то причине не нужно использовать 32-битную среду выполнения). Тогда используйте только ваши геттеры и сеттеры, вместо того, чтобы напрямую обращаться к синтезированным иварам. Следуя этому, вы избежите проблем с этой ошибкой в ​​будущем.

0 голосов
/ 26 июля 2011

Компилятор ведет себя правильно;синтез в подклассе с использованием хранилища в суперклассе является верботеном.

В какой-то момент произошла ошибка в этом файле против llvmЭто может быть в общедоступной базе данных ошибок.

В любом случае, пожалуйста, сообщите об ошибке с просьбой разъяснить это конкретное правило.

Я только что попробовал это, и он компилируется без предупреждения.Что я не делаю?

@interface MyBaseClass : NSObject {
    NSObject *fooInstanceVar;
}
@end

@interface MySubclass : MyBaseClass { }
@property (nonatomic, retain) NSObject *barProperty;
@end

@implementation MyBaseClass
@end

@implementation MySubclass
@synthesize barProperty;

- (void)doSomethingWithProperty {
    NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
    NSLog(@"%@", array);
}
@end

Не ясно, какую проблему вы пытаетесь решить.Все переменные экземпляра везде хрупкие, кроме 32-битной Mac OS X.

...