Как узнать, наследуется ли класс от NSObject (Objective-C)

Я работаю в Objective-C на iPhone, и мне нужно знать, наследуется ли «класс» от «NSObject».

Я попытался проверить, отвечает ли он на селектор NSObject:

bool success = [myClass respondsToSelector:@selector(class)];

но вы можете догадаться, что произошло ... он даже не ответил на «respondsToSelector:», поэтому выдает исключение «не реализует DoesNotRecognizeSelector:».

Я пытался перехватить это исключение, но похоже, что его нельзя перехватить с помощью @try-@catch.

Любые идеи?


person bendytree    schedule 22.11.2010    source источник
comment
Я думаю, что лучше спросить, как вы пришли к этим классам, которые не наследуются от NSObject. Не должно быть ничего, кроме протокола.   -  person Chuck    schedule 23.11.2010
comment
Вы имеете в виду @selector(class). Я не думаю, что в SDK есть что-то, что отвечает на +class:.   -  person tc.    schedule 23.11.2010
comment
Я создаю библиотеку, чтобы упростить сериализацию, а это значит, что я имею дело с классами, которые не создавал. Мне нужно как минимум убедиться, что класс соответствует протоколу NSObject, прежде чем я смогу с ним работать, иначе будет выдано неперехватываемое исключение (как Томми упоминал ниже).   -  person bendytree    schedule 23.11.2010
comment
@Dave DeLong: я как бы махал рукой, потому что NSProxy для NSObject действует как NSObject. Это не создает проблемы, когда вам нужно знать, какой у вас объект, чтобы знать, какие сообщения вам нужно отправить, чтобы спросить, что это за объект.   -  person Chuck    schedule 23.11.2010


Ответы (4)


Перейдите непосредственно к среде выполнения Objective-C:

#import <objc/runtime.h>

/* originally posted version — works because eventually class_getSuperclass(class)
returns nil, and class_getSuperclass(nil) does so also. */
BOOL classDescendsFromClass(Class classA, Class classB)
{
    while(1)
    {
        if(classA == classB) return YES;
        id superClass = class_getSuperclass(classA);
        if(classA == superClass) return (superClass == classB);
        classA = superClass;
    }
}

/* shorter version; exits straight after classA turns into nil */
BOOL classDescendsFromClassShorter(Class classA, Class classB)
{
    while(classA)
    {
        if(classA == classB) return YES;
        classA = class_getSuperclass(classA);
    }

    return NO;
}
...

if(classDescendsFromClass(classToTest->isa, [NSObject class]) ...

class_getSuperclass делает то, что говорит, и безопасно сравнивать метаклассы по указателю в среде выполнения Objective-C, потому что для каждого класса существует ровно один экземпляр метакласса. Указатель isa — это единственное, что обязательно находится в структуре objc_object.

РЕДАКТИРОВАТЬ: кроме того, в симуляторе iPhone есть известные ошибки, из-за которых некоторые исключения не перехватываются блоками try/catch. Я сообщил о них как об ошибке в Apple, и мне сказали, что мой дубликат, так что они определенно в курсе. Вы пробовали свой код на реальном устройстве или только в симуляторе?

РЕДАКТИРОВАТЬ2: из более широкого контекста, приведенного в другом месте этого разговора, что-то вроде этого может быть умнее:

#import <objc/runtime.h>

BOOL classRespondsToSelector(Class classA, SEL selector)
{
    return class_getInstanceMethod(classA, selector) ? YES : NO;
}

....
if(classRespondsToSelector(instance->isa, @selector(respondsToSelector:))
{
     // great, we've got something that responds to respondsToSelector:; do the
     // rest of our querying through there
}
person Tommy    schedule 22.11.2010
comment
Этот ответ превосходен и отлично работает. Вы правы и в отношении блоков try/catch на симуляторе/устройстве. Я читал, что блоки try/catch не следует использовать для управления потоком (в obj-c), плюс я не хочу терять отладку симулятора, поэтому я буду придерживаться этой концепции «getSuperclass». - person bendytree; 23.11.2010
comment
На самом деле это не совсем правильно; Я позволил себе немного запутаться — указатель isa NSObject указывает на NSObject (т. е. даже метакласс NSObject сам по себе является подклассом NSObject), но NSObject не является суперклассом самого себя. Итак, if(classA == superClass) завершает classDescendsFromClass на одну итерацию позже, чем необходимо (потому что в конечном итоге он получает nil как суперкласс nil). Я исправлю код. - person Tommy; 24.11.2010
comment
Если вы предпочитаете более похожий на Obj-C синтаксис: gist.github.com/BillinghamJ/6709374 - person James Billingham; 26.09.2013
comment
Вы можете использовать object_getClass(classA) вместо ->isa - person David Lawson; 30.04.2014

Вы можете использовать методы isKindOfClass: и isMemberOfClass:, чтобы определить, является ли класс подклассом другого класса или это отдельный класс.

person Jasarien    schedule 22.11.2010
comment
Эти методы являются частью протокола NSObject. Объекты, не соответствующие протоколу NSObject, могут не отвечать на эти сообщения. - person Greg; 23.11.2010
comment
Верно, но, как говорят все остальные, зачем вам иметь дело с объектами, которые не соответствуют протоколу NSObject? Это большой, гораздо более важный вопрос. У вас не должно быть причин не иметь объект, который не соответствует этому протоколу. - person Jasarien; 23.11.2010
comment
Что делать, если вы изучаете объект класса? - person James Billingham; 26.09.2013

respondsToSelector: сам по себе является селектором, определенным NSObject, поэтому вы не можете его использовать. Я не верю, что есть способ сделать это, не углубляясь во внутренности Objective-C.

Могу я спросить, почему у вас есть объекты, которые не являются потомками NSObject? Apple очень настоятельно рекомендует не пытаться их создавать, и на то есть веские причины.

person grahamparks    schedule 22.11.2010
comment
На самом деле, NSProxy — это класс Apple, который не наследуется от NSObject. Однако он соответствует протоколу NSObject, что более важно. - person cobbal; 23.11.2010
comment
Разумно предположить, что все будет реагировать на все, что определено в NSObject. NSProxy этого не делает, но ожидается, что он перенаправит на реальные объекты, которые это делают. - person tc.; 23.11.2010

Класс «Класс» не наследуется от NSObject. Это означает, что методы, определенные NSObject (такие как isKindOfClass или respondsToSelector), не могут быть использованы на нем.

Что вы пытаетесь сделать с ним в первую очередь?

person David Liu    schedule 22.11.2010
comment
-1 это так. Class является экземпляром метакласса, например. -NSObject является экземпляром +NSObject, надклассом которого является -NSObject, поэтому -NSObject является экземпляром -NSObject. Я не уверен, какие метаклассы являются экземплярами (возможно, также NSObject!), или что произойдет, если вы используете другой корневой класс (т.е. если вы не наследуете от NSObject), но метаклассы являются основой для методов класса (в отличие от к статическим методам, у которых нет self). - person tc.; 23.11.2010