С каких это пор можно объявить Objective-C 2.0 свойства в категории?

Я всегда думал, что нельзя объявлять свойство объекта в категории. Пока мой партнер не сделал это в нашем коде приложения, и он, похоже, работал.

Я продолжал говорить о том, что вы пытаетесь объяснить ему, что нет, Objective-C можно использовать только для добавления методов, а не свойств. Я нашел такие вопросы, как:

Но затем я нашел эту ссылку на сайте Apple, который содержит следующее о объявлении @property:

Объявление свойства начинается с ключевое слово @property. @property может появляются в любом месте метода список деклараций, найденный в @interface класса. @property может также появляются в декларации протокола или категории. (выделено мной)

Я знаю, что это не работает:

@interface MyClass ()
NSInteger foobar;
- (void) someCategorizedMethod;
@end

Но это компилируется:

@interface MyClass ()
@property NSInteger foobar;
- (void) someCategorizedMethod;
@end

Мой вопрос: (a) что лучше всего здесь? и (b) это что-то новое для Objective-C 2.0, и вместо использования "реального" iVar он просто использует ассоциативную память за кулисами, чтобы сделать эту работу?

Ответы

Ответ 1

Вы всегда можете объявить @property в категории. То, что вы не могли сделать - и все еще не можете - объявить хранилище для свойства в категории, ни как переменную экземпляра, ни через `@synthesize.

Однако....

@interface MyClass () не является категорией. Это расширение класса и имеет значительно более конкретную роль, чем категория.

А именно, расширение класса можно использовать для расширения класса @interface, и это включает в себя @properties, которые могут быть @synthesized (включая синтез хранилища в современной среде исполнения).

Foo.h:

@interface Foo
@end

Foo.m:

@interface Foo()
@property int x;
@end

@implementation Foo
@synthesize x; // synthesizes methods & storage
@end

он просто использует ассоциативное хранилище за кадром, чтобы сделать эту работу?

Нет - это реальная переменная экземпляра. Современная среда выполнения устраняет проблему хрупкого базового класса.


@interface MyClass ()
NSInteger foobar;
- (void) someCategorizedMethod;
@end

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

Если вы измените его на:

@interface MyClass () {
    NSInteger foobar;
}
- (void) someCategorizedMethod;
@end

Затем он будет работать с последней версией компилятора llvm (с правильными флагами, как указано в комментарии @Joshua).

Ответ 2

Вообще говоря, свойства ничем не отличаются от других методов. До тех пор, пока используемый ivar доступен в обычном классе, нет никаких проблем. Это просто синтаксический сахар.

Все начинает усложняться, если также автоматически создается ivar, как это возможно в некоторых конфигурациях.

Главное, что объявление ivar не зависит от свойства.

Ответ 3

Ассоциативное хранилище - это решение. Посмотрите на post.