Скрытие свойств из общего доступа
Я пытаюсь объявить свойства, которые предназначены для внутреннего использования только в категории 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, найденный в интерфейсе
Каким будет правильный способ?
Ответы
Ответ 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, чтобы хотя бы добавить некоторую защиту к его состоянию.