Параметр Introspect типа: id, чтобы решить, класс это или протокол

У меня есть такой способ:

-(void)SomeMethod:(id)classOrProtocol;

Он будет называться так:

[self someMethod:@protocol(SomeProtocol)];

Or

[self someMethod:[SomeClass class]];

В теле метода мне нужно решить, | classOrProtocol | является:

Любой класс (класс) ИЛИ Любой протокол (протокол) ИЛИ что-нибудь еще

[[classOrProtocol class] isKindOfClass: [Protocol class]]

Приводит к ошибке (сборки):

Receiver 'Protocol' - это прямой класс, и соответствующий @interface может не существовать

Итак, как я могу отличить протокол от класса от чего-либо еще?


person Undistraction    schedule 21.09.2011    source источник


Ответы (2)


В Objective-C 2 (т.е. если вы не используете 32-битную среду выполнения в OS X) Protocol определен как просто прямой класс, см. /usr/include/objc/runtime.h. Настоящий интерфейс нигде не заявлен. Вы можете попробовать включить /usr/inlcude/objc/Protocol.h, сказав

#import <objc/Protocol.h>

но, как там написано, для экземпляра Protocol публично не поддерживается ни один метод. Единственный приемлемый способ работы с экземплярами Protocol - использовать функции времени выполнения, указанные в Справочник по среде выполнения Objective-C. Это даже не определено публично, является ли Protocol подклассом чего-либо, и даже не указано, что он реализует протокол NSObject. Таким образом, вы не можете вызвать для него какой-либо метод.

Конечно, вы можете использовать исходный код среды выполнения, чтобы увидеть, что происходит. Protocol наследуется от Object (который является остатком от NeXTSTep до OpenStep), а не от NSObject. Таким образом, вы не можете использовать знакомые методы для объектов, производных от NSObject, включая Class из объектов, производных от NSObject. См. Реализации с открытым исходным кодом Protocol.h и Protocol.m. Как вы видите, сам класс Protocol ничего не делает, потому что каждый метод просто приводит self к protocol_t и вызывает функцию. Фактически, как видно из функции _read_images и других в _ 19_, указатель isa объекта Protocol устанавливается вручную при загрузке исполняемого файла и библиотек и никогда не используется.

Так что не пытайтесь проверить, является ли id Protocol или нет.

Если вам действительно нужно это сделать, вы можете использовать

id foo=...;
if(foo->isa==class_getClass("Protocol")){
    ...
}

Но, серьезно, не делай этого.

person Yuji    schedule 21.09.2011
comment
Спасибо за исчерпывающий ответ. Я использую этот метод в коде, связанном с внедрением зависимостей, и в этом контексте я думаю, что это имеет смысл. Но, возможно, мне следует разделить это на два метода. - person Undistraction; 21.09.2011
comment
Добавил немного больше информации о внутреннем. Было весело читать исходный код, спасибо за хороший вопрос, 1ndivisible! - person Yuji; 21.09.2011

Это не проблема, вызванная невозможностью определить, класс это или протокол. Ошибка вызвана отсутствием интерфейса класса Protocol. Убедитесь, что вы импортируете Protocol.m в начало файла реализации, в котором вы проверяете тип аргумента.

Вы также можете попробовать использовать функцию NSClassFromString(), которая вернет объект Class или nil. Однако обратите внимание, что если возвращается nil, это не означает, что аргумент является протоколом. Это просто означает, что это тоже может быть неопределенный класс!

Также существует метод NSProtocolFromString, который возвращает соответствующие результаты - протокол для протокола и nil для неопределенного протокола.

person Eimantas    schedule 21.09.2011
comment
Спасибо за ответ. Если я попытаюсь импортировать Protocol.m (или Protocol.h), я получаю сообщение об ошибке «Файл не найден». Я также могу использовать протокол в качестве типа параметра в том же файле без какого-либо импорта. - person Undistraction; 21.09.2011
comment
Правильно, я подумал, что это немного чепуха, поскольку Protocol - это класс для фактического типа протокола, который не имеет файлов Protocol. (H | m) как таковых. Извините, что сбил вас с толку. - person Eimantas; 21.09.2011