IOS开发知识点

isKindOfClass:与isMemberOfClass:

2020-09-17  本文已影响0人  过客Zhaopy

前言

对于iOS开发者而言,isKindOfClass:isMemberOfClass:应该是相当熟悉的,今天我们不是要讲这两个方法的用法,而是讨论一个关于这两个方法的面试题。

正文

大家思考一下下面这个面试题:

BOOL result1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL result2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL result3 = [(id)[PYTeacher class] isKindOfClass:[PYTeacher class]];
BOOL result4 = [(id)[PYTeacher class] isMemberOfClass:[PYTeacher class]];

其实分析这个面试题,只要根据 isa 的指向图,加以分析就能得出正确的结论:

result1 = YES;
result2 = NO;
result3 = NO;
result4 = NO;

分析过程:

NSObject类对象 属于 NSObject元类NSObject元类父类NSObject类,所以result1 = YESresult2 = NO
同理:
PYTeacher类对象 属于 PYTeacher元类,但是在 PYTeacher元类继承链 中不包含 PYTeacher类,所以result3 = NOresult4 = NO

小结

本来分析到这里,这篇文章也就该结束了。但是,笔者曾亲身经历过这个面试题,也是这样分析的,但面试官始终追问一句:你有没有看过 isKindOfClass: 的实现?
今天,同样把这个问题抛给大家:你有没有看过 isKindOfClass: 的实现?

isKindOfClass:

我们可以通过 objc源码查看 isKindOfClass: 的具体实现:

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

so easy! 就这么简单!一个 for 循环而已! 😎😎😎
让我们运行一下,在isKindOfClass:里打个断点:

图1
这时,意外发生了!!!
程序并没有停在断点这里!!!
什么情况?方法为什么没有执行?😱😱😱

这个方法没有执行,肯定是出了什么问题,那能是什么问题呢?我们只是运行了一下代码而已,难道是编译器做了手脚?

LLVM

刚才我们想到了编译器,那我们就从 LLVM 里面寻找答案。我们可以在LLVM的代码里搜索isKindOfClass,会找到如下内容:

// This is the table of ObjC "accelerated dispatch" functions.  They are a set
// of objc methods that are "seldom overridden" and so the compiler replaces the
// objc_msgSend with a call to one of the dispatch functions.  That will check
// whether the method has been overridden, and directly call the Foundation 
// implementation if not.  
// This table is supposed to be complete.  If ones get added in the future, we
// will have to add them to the table.
const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
    "objc_alloc",
    "objc_autorelease",
    "objc_release",
    "objc_retain",
    "objc_alloc_init",
    "objc_allocWithZone",
    "objc_opt_class",
    "objc_opt_isKindOfClass",
    "objc_opt_new",
    "objc_opt_respondsToSelector",
    "objc_opt_self",
};

注释的大体意思就是,这是个加速调度的函数表,这里面是一些很少被覆盖的objc的方法,所以编译器会用他们替换objc_msgSend。我们发现其中有一个objc_opt_isKindOfClass,我们猜测编译器会用他来替换isKindOfClass:方法。我们在 objc 的源码中搜索这个方法,并打一个断点。

图2
如图2中,果然像我们猜测的那样,调用了 objc_opt_isKindOfClass 方法,而这个方法的主要内容也是一个 for 循环,至此 isKindOfClass: 我们就分析完了。😂

isMemberOfClass

isMemberOfClass的源码如下:

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

isMemberOfClass的源码并不复杂,这里就不再赘述了。👨‍🏫

总结

当我们在调用isKindOfClass:方法的时候,编译器已经把方法替换了,实际运行的时候,会调用 objc_opt_isKindOfClass 方法,方法内部会通过一个 for 循环来追溯,对象所属的类在不在目标类的继承链中。

上一篇下一篇

猜你喜欢

热点阅读