В objective-C (iphone), как мне управлять памятью ссылок "@protocol"?
Я думал, что у меня неплохой инструмент управления памятью для objective-c, но я не могу понять следующую ситуацию:
@protocol MyProtocol
@end
@interface MyObject : NSObject {
id<MyProtocol> reference;
}
@property (nonatomic, retain) id<MyProtocol> reference;
@end
@implementation MyObject
@synthesize reference;
-(void) dealloc {
[reference release];
[super dealloc];
}
...
@end
Это дает мне предупреждение : '-release' не найден в протоколе.
Можно ли игнорировать эту ошибку? Или я делаю что-то ужасно неправильно?
Ответы
Ответ 1
Да, вы можете спокойно проигнорировать эту ошибку. Объект, объявленный как тип id<MyProtocol>
, может не наследоваться от NSObject
(вам не нужно использовать библиотеки Cocoa для программирования в Objective-C, и есть другие корневые классы даже в Cocoa, например NSProxy
), Поскольку retain
(и release
, autorelease
) объявлены в NSObject
, компилятор не может знать, что экземпляр, объявленный как тип id<MyProtocol>
, отвечает на эти сообщения. Чтобы обойти это, Cocoa также определяет протокол NSObject
, который отражает API NSObject
. Если вы объявите свой протокол как
@protocol MyProtocol <NSObject>
@end
указывающий, что MyProtocol
расширяет протокол NSObject
, вы будете установлены.
Ответ 2
Обычно, когда вы объявляете объект как id
, он подсчитывает "любой" объект (это означает, что Objective-C позволит вам вызывать любой метод из любого класса или протокола на id
без предупреждения).
Однако, когда вы объявляете объект как id<SomeProtocol>
, значение изменяется. В этом случае вы говорите: я вызову только методы SomeProtocol
для этого объекта.
Метод:
- (void)release;
объявлен в протоколе NSObject
, но вы явно указали: я буду использовать только методы MyProtocol
. Поэтому компилятор дает вам предупреждение, чтобы сообщить вам, что вы нарушили свое собственное обещание.
Следовательно, вместо:
id<MyProtocol> reference;
вы должны фактически объявить:
id<MyProtocol, NSObject> reference;
или
NSObject<MyProtocol> reference;
поскольку NSObject
(класс) реализует NSObject
(протокол).
или
id reference;
который является самым широким из множества: позвольте мне вызывать что-либо на этом объекте и никогда не жалуюсь.
Вы также можете (как предположил Барри Уорк) иметь MyProtocol
включить протокол NSObject
- хотя с точки зрения дизайна вы обычно это делаете, только если реализация MyProtocol
обязательно означает использование NSObject
. Обычно мы делаем это только в том случае, если NSObject
и MyProtocol
связаны по наследству или семантически.
Немного информации о протоколе NSObject
:
Все, что вы ссылаетесь на сохранение/освобождение/автоопределение, должно реализовать этот протокол. Как вы можете это сделать: в основном все реализует протокол NSObject
(хотя некоторые из них не относятся к базовому классу NSObject
).
Еще одно быстрое пояснение: NSObject
(класс) и NSObject
(протокол) не являются повторными реализациями одного и того же API. Они разбиваются следующим образом:
-
NSObject (протокол) реализует все необходимое для обработки/проверки существующего объекта в общем смысле (сохранение/освобождение, isEqual, класс, responseSToSelector: и т.д.).
-
NSObject (класс) реализует менее общие методы: построение/уничтожение, интеграция потоков, интеграция сценариев.
Таким образом, в большинстве случаев протокол является более важным из двух. Помните, что класс включает протокол, поэтому, если вы сходите из NSObject, вы получаете оба.
Ответ 3
Изменить
@property (nonatomic, retain) id<MyProtocol> reference;
to
@property (nonatomic, assign) id<MyProtocol> reference;
Зачем пытаться сохранить объект, реализующий протокол? Все, что вам нужно, это указатель на объект, с помощью которого мы можем вызывать методы, объявленные в вашем протоколе.