Сокрытие свойств от публичного доступа - PullRequest
24 голосов
/ 13 апреля 2009

Я пытаюсь объявить свойства, предназначенные только для внутреннего использования, в категории 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

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

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

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

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

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

Ответы [ 6 ]

42 голосов
/ 13 апреля 2009

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

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

См. документы Apple по расширениям , чтобы узнать больше об этом. (Очевидно, это новое в Mac OS 10.5.)

РЕДАКТИРОВАТЬ: Пример:

// 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 времени выполнения .

12 голосов
/ 15 июля 2010

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

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

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

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

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

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

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

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

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

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

@implementation MyClass
@synthesize targetView;
@end

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

8 голосов
/ 13 апреля 2009

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

2 голосов
/ 03 ноября 2011

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

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

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

Приветствия

1 голос
/ 21 октября 2011

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

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

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
1 голос
/ 13 апреля 2009

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

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

@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, чтобы хотя бы добавить некоторую защиту в его состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...