美文网首页IOS开发知识点
isKindOfClass:与isMemberOfClass:

isKindOfClass:与isMemberOfClass:

作者: 过客Zhaopy | 来源:发表于2020-09-17 23:15 被阅读0次

    前言

    对于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 循环来追溯,对象所属的类在不在目标类的继承链中。

    相关文章

      网友评论

        本文标题:isKindOfClass:与isMemberOfClass:

        本文链接:https://www.haomeiwen.com/subject/fxvryktx.html