isKindOf与isMemberOf的原理

本文大约1000字,将会讲述自己关于isKindOf与isMemberOf的原理方面的思考

!!!原创文章,转载请注明来源: pany.fun

# 是什么

面对新的东西,我们第一个思考的问题往往都是——它是什么。

虽然 isKindOf 与 isMemberOf 对于我们来说已经是万分熟悉了,但我们依旧希望 系统而形式 的先来认识它们是什么。

// NSObject.h -> line: 30

@protocol NSObject

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;

这里首先我们需要注意到,它们是定义在 NSObject协议 中的方法,而不是定义在 NSObject类 中的方法,虽然NSObject类默认实现了它们。

官方文档对于二者的描述

  • isKindOfClass:

Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.

​ 返回对于 接收者是否是所给类或者所给类的子类的实例 的判断结果

  • isMemberOfClass:

Returns a Boolean value that indicates whether the receiver is an instance of a given class.

​ 返回对于 接收者是否是所给类的实例 的判断结果

两个方法异同点如下

  • 都能够判断 接收者是否是所给类本身的实例
  • isKindOfClass还能够判断接收者是否是所给类的子类的实例,而isMemberOfClass不能

以上内容相对简单,网上搜一搜能找到很多,此文不再多说。

# 为什么

我们的第二个问题——为什么,我们面对新事物往往也会有这样的好奇。

为什么 isKindOfClass 能够进行是否属于子类实例的判断?而isMemberOfClass不能?它们做了什么不一样的事情?

当然,两个不同方法是开发工作的需要,但是这不是我们所想给出的答案,我们更多想知道的是它们是如何去判断的,在判断上又有什么不同。

# 答案

为了理解它们的原理,我们需要先引入一张熟悉的图,如果这张图你不熟悉,请点击这里 或者google

对象,类与元类之间的关系

图中Root Class(class)可以理解为NSObject,Root Class(meta)可以理解为NSObject meta

更精准的描述 NSObject: The root class of most Objective-C class hierarchies

isMemberOf 与 isKindOf的判断逻辑,就藏身于这张图中。

  • isMemberOf:

    step1: 找到 isa 指针所指类

    step2: 判断 isa所指类 与 所给类 是否相同

  • isKindOf:

    step1: 找到 isa 指针所指类

    step2: 判断 isa所指类 与 所给类 是否相同,若不相同 则延该类的继承链(实线)不断向上查询直至nil

两个方法的第一步都是相同的,差别只是在第二步,因为isKindOf多了对于子类的实例的判断,所以也就需要增加继承链的查询。

# 源码

同时因为runtime是开源的,所以我们也可以去查看NSObject源码看是不是这么回事

  • isMemberOf 源码

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

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

对比可以发现,isKindOf确实相较isMemberOf多了一个循环,也就是延继承链的查询

# 扩展

在孙源的一篇 神经病院objc runtime入院考试 中有这么一道题目,我们一起以上述原理来看看

(这里为了方便表述,交换了一下几个题目的顺序)

BOOL res1 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  // 原孙源博客中res2
BOOL res2 = [(id)[NSObject class] isKindOfClass:[NSObject class]];    // 原孙源博客中res1
BOOL res3 = [(id)[Sark class] isMemberOfClass:[Sark class]];    // 原孙源博客中res4
BOOL res4 = [(id)[Sark class] isKindOfClass:[Sark class]];    // 原孙源博客中res3
  • Round 1

    接收者为 NSObject ,即图中的 下标A处。取isa,指向 下标B处,并不是NSObject,所以结果为 NO

  • Round 2

    在round 1的基础上继续,已经取到了isa指向 下标B处 且isMemeberOf结果为NO,判断isKindOf接下来需要走继承链,延B的实线走,来到了A处,即NSObject,等于所给类NSObject,所以结果为 YES

  • Round 3

    接收者为 Sark,可以理解为一个继承自NSObject的类,即图中 C 处。取isa指针,指向D,并不是所给的NSObject类,所以结果为NO

  • Round 4

    在round 3的基础上,继续沿D的实线继承链走,经过B、A最终走向nil,始终没有找到所给的Sark类,所以结果为NO

创作不易,转载请注明来源!pany.fun
本文链接:http://pany.fun/post/isKindOf与isMemberOf的原理/