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

В 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' не найден в протоколе. Можно ли игнорировать эту ошибку? Или я делаю что-то ужасно неправильно?

4b9b3361

Ответ 1

Да, вы можете спокойно проигнорировать эту ошибку. Объект, объявленный как тип id<MyProtocol>, может не наследоваться от NSObject (вам не нужно использовать библиотеки Cocoa для программирования в Objective-C, и есть другие корневые классы даже в Cocoa, например NSProxy), Поскольку retainrelease, 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;

Зачем пытаться сохранить объект, реализующий протокол? Все, что вам нужно, это указатель на объект, с помощью которого мы можем вызывать методы, объявленные в вашем протоколе.