Использование расширений класса в xcode 4.4

Начиная с xcode 4.4 вам больше не нужны свойства @synthesize (см. здесь), компилятор сделает это за вас. Итак, почему компилятор жалуется

использование необъявленного идентификатора _aVar

в моем viewDidLoad методе ViewControllerSubclass:

@interface ViewController : UIViewController
@property (assign, nonatomic) int aVar;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.aVar = 5;
    NSLog(@"Super value: %d", _aVar);
}
@end

@interface ViewControllerSubclass : ViewController
@end

@interface ViewControllerSubclass ()
@property (assign, nonatomic) int aVar;
@end

@implementation ViewControllerSubclass
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"Subclass value: %d", _aVar);
}
@end

Если я перемещаю все в один файл вместо 4 отдельных файлов для соответствующих интерфейсов и реализаций, компилятор вместо этого жалуется, что _aVar является закрытым. Но _aVar должен был быть автоматически синтезирован в моем ViewControllerSubclass.

Все еще сохраняя все в 1 файле, если я перенесу начальное объявление свойства в расширение класса:

@interface ViewController ()
@property (assign, nonatomic) int aVar;
@end

Сборка по-прежнему не работает, говоря, что _aVar является закрытым.

Если я вернусь к настройке 4 файлов для соответствующих интерфейсов и реализаций, xcode строит даже без предупреждения.

Если я затем запускаю код:

[[[ViewControllerSubclass alloc] init] view];

операторы журнала в приведенных выше примерах выводят следующее:

Супер ценность: 0

Значение подкласса: 5

Имеет смысл, что NSLog(@"Super value: %d", _aVar); дал результат 0, поскольку предполагается, что эта переменная является частной для суперкласса. Но тогда почему NSLog(@"Subclass value: %d", _aVar); дает результат 5??

Все это очень странно.


person Remover    schedule 26.08.2012    source источник
comment
Насколько я понимаю, это функция, которая является частью компилятора Apple LLVM 4.0, а не Xcode IDE. Какой компилятор вы используете в своем проекте?   -  person Pedro Mancheno    schedule 26.08.2012
comment
Да, но xcode4.4 использует этот компилятор по умолчанию.   -  person Remover    schedule 26.08.2012
comment
Таким образом, конфигурация, которая создает, но генерирует нечетные операторы журнала, — это та, в которой вы объявили свойство в общедоступном интерфейсе суперкласса и расширении подкласса?   -  person Carl Veazey    schedule 26.08.2012
comment
Нет, единственная «конфигурация», которая создается, — это та, в которой оба свойства объявлены в расширениях класса как для суперкласса, так и для подкласса.   -  person Remover    schedule 26.08.2012


Ответы (3)


Когда вы делаете объявление суперкласса общедоступным, компилятор не будет пытаться повторно синтезировать свойство; предполагается, что об этом позаботились в суперклассе. Таким образом, _aVar нигде в подклассе не рассматривается. В любом случае это личное, поэтому даже когда вы помещаете их все в один и тот же файл, вы видите эти ошибки.

Однако, когда вы делаете объявление свойства суперкласса внутри расширения класса, компилятор автоматически синтезирует свойство как для суперкласса, так и для подкласса. Это приводит к тому, что оба класса имеют частные переменные экземпляра _aVar (с двумя разными адресами). Однако, когда метод суперкласса viewDidLoad устанавливает свойство, метод вызывает методы доступа подкласса, которые устанавливают значение частной переменной _aVar подкласса, а не суперкласса. Так что это объясняет, почему вы видите, что значение суперкласса не меняется.

Надеюсь это поможет!

person Carl Veazey    schedule 26.08.2012
comment
Мой вопрос действительно состоял из нескольких разных вопросов, и это отвечает на них все. - person Remover; 26.08.2012
comment
Рад, что смог вам помочь! Спасибо! - person Carl Veazey; 26.08.2012

Вы путаете несколько разных проблем, и я несколько сбит с толку, когда вы говорите о переходе между файлами и не указываете, где происходят ваши ошибки.

В любом случае, существует проблема видимости переменной экземпляра. Если вы объявляете свои iVars в области интерфейса, они по умолчанию защищены.

@interface Foo : NSObject {
    int protectedInt;
@private
    int privateInt;
@public
    int publicInt;
}
@end

Когда вы синтезируете iVars, сами переменные экземпляра являются частными, если только вы не укажете их явно.

Методы всегда будут запускаться в наиболее производной реализации.

Теперь, когда вы называете это...

[[[ViewControllerSubclass alloc] init] view];

Вы выделите подкласс, инициализируете и вызовете загрузку представления. Этот код будет выполняться...

@implementation ViewControllerSubclass
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"Subclass value: %d", _aVar);
}
@end

Первое, что он делает, это вызывает реализацию базового класса...

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.aVar = 5;
    NSLog(@"Super value: %d", _aVar);
}
@end

Конечно, это называется супер, но здесь это не важно. Следующая строка присваивает 5 переменной self.iVar. Но какой iVar? Он вызывает метод установщика свойств для этого объекта. Какого типа этот экземпляр? Это ViewControllerSubclass. Поскольку вы дали и базовому классу, и его подклассу одно и то же имя (и объявили свойство как часть расширения класса), у каждого из них есть собственная переменная экземпляра частной области видимости.

Однако метод вызывается для наиболее производной реализации. Таким образом, self.iVar установит переменную экземпляра подкласса. Переменная экземпляра для базового класса остается неизменной.

Когда вы NSLog значение, вы получаете доступ к закрытой переменной экземпляра базового класса, которая не была изменена.

Теперь, после завершения базового класса viewDidLoad, мы запускаем код для подкласса. Он регистрирует значение своей частной переменной экземпляра, которая была изменена в результате вызова базовым классом средства установки свойства. Итак, теперь он напечатает свое значение, равное 5.

person Jody Hagins    schedule 26.08.2012

Я только что проверил вашу настройку и смог воспроизвести вашу ошибку. Я пришел к следующему выводу:

Вам нужно объявить @property в файле .h. Если вам нужна частная переменная, объявите ее в .m в категории @interface (та, что в скобках).

person Mundi    schedule 26.08.2012
comment
Вот в чем дело. В последнем примере выше aVar объявлен в .m для обоих классов, так почему же подкласс выводит значение 5? - person Remover; 26.08.2012