从Runtime角度看OC中的 isKindOfClass 和 isMemberOfClass

isKindOfClassisMemberOfClass这两个方法常用,但是最近倒腾的时候发现了一些有趣的东西,也去网上找了一下资料,以下是对这篇资料的摘抄。



首先,思考下下面四个结果打印出来后哪个为真哪个为假?

BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[TestObject class] isKindOfClass:[TestObject class]];
BOOL res4 = [(id)[TestObject class] isMemberOfClass:[TestObject class]];

答案:除了第一个是YES,其他三个都是NO。

isMemberOfClass显而易见,那isKindOfClass是不是很有趣,为什么呢?
看看Runtime的源码能给我们什么思路:

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

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

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



我们侧重看isKindOfClass的类方法实现,需要结合Runtime的对象模型:

Runtime的对象模型

因为调用类的class方法,会把类自身直接返回,所以第一次调用者是NSObject类对象,+ (BOOL)isKindOfClass:(Class)cls方法的参数cls也是NSObject类对象。

进入到for循环中,在此说明一下object_getClass()方法相当于取出isa指针指向的类,所以会从NSObject的元类开始遍历,所以第一次NSObject meta class != NSObject class,匹配失败。第二次循环将tcls 设置为superclassNSObject classNSObject class==NSObject class,匹配成功。

NSObject 能匹配成功,是因为这个类比较特殊,在第二次获取 superClass的时候,NSObject 元类的superClass就是NSObject的类对象,所以会匹配成功。

TestObject之所以匹配失败,由图所示,其isa指向其元类,元类匹配失败后,取其 superClass即为Root Class (meta),依旧为元类,再次匹配至Root Class(class),然后结束,全都没有匹配到,所以匹配失败。

一开始我对这个现象也是百思不得其解,不过看了资料的说明后,恍然大悟。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容