Подтвердить что ты не робот

Скрытие свойств из общего доступа

Я пытаюсь объявить свойства, которые предназначены для внутреннего использования только в категории Private как таковые:

@interface BarLayer (Private)

@property (readwrite, retain) MenuItemFont  *menuButton;
@property (readwrite, retain) Menu          *menuMenu;
@property (readwrite, retain) LabelAtlas    *messageLabel;

@end

Теперь я пытаюсь выяснить, где именно я должен @synthesize.

Я пробовал:

@implementation BarLayer (Private)

@synthesize menuButton      = _menuButton;
@synthesize menuMenu        = _menuMenu;
@synthesize messageLabel    = _messageLabel;

@end

Здесь компилятор жалуется:

@synthesize не разрешено в реализации категории

Поэтому я попытался поместить его в мою реализацию BarLayer, но здесь он не находит объявления в интерфейсе BarLayer.

нет объявления свойства 'menuButton, найденный в интерфейсе

Каким будет правильный способ?

4b9b3361

Ответ 1

Собственно, с последним компилятором LLVM эта проблема может быть решена намного лучше. Ранее рекомендованный метод скрыть как можно больше о ваших свойствах заключался в том, чтобы объявить ваши переменные в вашем .h, префикс их с помощью _, объявить свойство в расширении класса в частном .m и @synthesize это свойство в вашем @реализация.

С последним LLVM (3.0) вы можете идти еще дальше, скрывая все о вашей собственности, включая поддержку ivar. Его объявление может быть перемещено в .m и даже опущено там, и в этом случае он будет синтезирован компилятором (спасибо Ivan):

Car.h:

@interface Car : NSObject

- (void)drive;

@end

Car.m:

@interface Car ()

@property (assign) BOOL driving;

@end

@implementation Car
@synthesize driving;

- (void)drive {

    self.driving = YES;
}

@end

Ответ 2

Вы не можете использовать @synthesize с категорией.

Вы можете сделать это с расширением класса (анонимная категория a.k.a.), которая является просто категорией без имени, методы которой должны быть реализованы в основном блоке @implementation для этого класса. Для вашего кода просто измените "(частный)" на "()" и используйте @synthesize в основном блоке @implementation вместе с остальной частью вашего кода для этого класса.

Подробнее об этом см. документы Apple на расширениях. (По-видимому, это новое в Mac OS 10.5.)

EDIT: Пример:

// Main interface (in .h)
@interface Foo : NSObject
- (void)bar;
@end

// Private interface (in .m, or private .h)
@interface Foo ()
@property (nonatomic, copy) NSString *myData;
@end

@implementation Foo
@synthesize myData; // only needed for Xcode 4.3 and earlier
- (void)bar { ... }
@end

Другим решением, которое намного больше работает, является использование objc_setAssociatedObject и objc_getAssociatedObject для подделки дополнительных переменных экземпляра. В этом случае вы можете объявить их как свойства и сами реализовать сеттеры и геттеры, используя вышеописанные методы выполнения objc_ *. Подробнее об этих функциях см. документы Apple в Objective-C время выполнения.

Ответ 3

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

Следующая информация поступает из http://www.friday.com/bbum/2009/09/11/class-extensions-explained/

"Синтез причины был запрещен в категориях, потому что синтез требует хранения, и не было возможности эффективно объявлять хранение в категории, и было сочтено приемлемым, чтобы позволить категории синтезировать методы, которые затем могли бы влиять на классы ivars напрямую. хрупкой и уродливой.

Однако было также очевидно, что было бы возможно объявить свойство, которое было открыто для чтения только, но чья реализация была прочитана для целей внутри-класса или рамки.

Еще одно требование состоит в том, что синтез таких свойств должен всегда иметь возможность синтезировать как сеттер, так и геттер естественно и точно. В частности, при объявлении свойства как атомарного, разработчик не может правильно вручную написать только 1/2 пары установителей геттера; инфраструктура блокировки не открывается и, таким образом, нет возможности гарантировать атомарность в такой ситуации.

Расширения класса исправляли эту проблему элегантно.

В частности, вы можете объявить свойство вроде:

@interface MyClass : NSObject
@property(readonly) NSView *targetView;
@end

И затем в файле реализации:

@interface MyClass()
@property(readwrite) NSView *targetView;
@end

@implementation MyClass
@synthesize targetView;
@end

Конечный результат? Свойство, которое публично читается только, но конфиденциально читается без открытия свойств вплоть до всей хрупкости удовольствия, связанной с категориями.

Ответ 4

Скотт Стивенсон (http://theocacao.com/) объясняет в своем сообщении в блоге "Быстрый Objective-C 2.0 Tutorial: Part II" , как получить Открытые свойства с частными сеттерами. Следуя его совету, вы получите свойство, доступное только для чтения, но имеющее частный сеттер, который можно использовать с точечным синтаксисом. Надеюсь, это поможет...

Ответ 5

Я просто хочу добавить свои 2 цента и дать людям понять, что возможно добавлять свойства к существующему классу через категории (не расширения классов). Это требует использования ассоциативных ссылок, но это действительно не так уж плохо.

Я написал сообщение об этом здесь, если кто-то хочет получить более подробную информацию.

Есть еще один вопрос, который затрагивает эту тему, но это довольно скудно в деталях.

Приветствия

Ответ 6

Поскольку категории могут добавлять методы только к классу, вы не можете обойти это, пытаясь определить методы свойств в категории.

Вы можете объявить свойства, полученные из уже существующих классов. Например. Если ваш класс имеет свойство firstName и lastName, вы можете объявить свойство с именем fullName в категории @implementation.

@interface Bar (Private)
@property (readonly) NSString *fullName; // Note readonly, you have nothing to write to.
@end

Однако вы не можете просто @synthesize это свойство, вам придется написать свой собственный аксессуар, потому что компилятор понятия не имеет, откуда вы хотите получить это значение.

@implementation Bar (Private)
- (NSString *)fullName {
    NSString *returnString = [NSString stringWithFormat:@"%@ %@", 
                             self.firstName, self.lastName];
}

С точки зрения дизайна класса я не уверен, что идея частной собственности имеет смысл: я лично думаю о свойствах как о том, что публично раскрывается классом.

вы можете использовать ключевое слово @private в классе BarLayer, чтобы хотя бы добавить некоторую защиту к его состоянию.