isKindOfClass 和 isMemberOfClass 史上最详细讲解

633 阅读6分钟

准备工作

isa 走位图

如果想讲明白这两个方法的区别, 那就必须得有这张图. 有了这张图, 大家可以理解的更透彻, 更明白. 实际这张图展示的是 isa 走位 和继承关系, 我习惯叫 isa 走位图; 我对于这张图的理解有两点;

  1. isa走位 : 对象的 isa 指向类, 类的 isa 指向元类, 元类的 isa 指向自己
    • 对象 -> 类 -> 元类 -> 根元类 <-> 根元类
  2. 继承关系 : 所有类(包括元类)都是 NSObject 的子类; 继承关系只存在于类与类之间;
    • 子类 -> 父类 -> ... -> NSObject
    • 子类的元类 -> 父类的元类 -> ... -> NSObject 的元类 -> NSObject

注意:
NSObject 的元类是根元类, 根元类的父类是 NSObject image.png

objc 源码

有了图, 再加上源码, 肯定是如虎添翼, 如鱼得水, 让你尽情的飞, 尽情的游. 这两个方法在上层接口中暴露出来是的两个对象方法, 实际在底层的实现分别有类方法和对象方法两种, 当类调用的时候, 底层走的是类方法, 类也可以叫类对象, 类对象是元类的实例对象, 这个大家应该不陌生. 下面是底层实现的代码.

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

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

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

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

对象方法分析

- (BOOL)isMemberOfClass:(Class)cls

这个方法实现还是比较简单的, self(调用者)的类型与 cls 是否是同一个类, 是就返回 YES, 不是就返回 NO; 只对比本身的元类, 不会沿着继承链向上查找; 因为每个类对象在内存中只存在一份, 所以可以用 == 判断;

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

- (BOOL)isKindOfClass:(Class)cls

这个方法实现也不是很复杂, 首先判断的是 self(调用者)的类型与 cls 是否是同一个类, 是就返回YES, 不是就循环取出对应的父类继续比较, 一直沿着继承链向上找到 NSObject, 如果还不是就取 NSObject 的父类, 因为 NSObject 的父类指向的是 nil, 所以循环结束. 如果到循环结束都没有返回, 最后就返回 NO; 简单来说就是从本类开始, 沿着继承链向上找, 只要有一个类与 cls 是同一个类, 就返回 YES.

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

类方法分析

注意: self(调用者) 是, 或者说类对象', 元类也是类.

+ (BOOL)isMemberOfClass:(Class)cls

这个类方法的实现其实与对象方法类似, 只是他对比的是元类, self(调用者)的元类型与 cls 是否是同一个类, 是就返回 YES, 不是就返回 NO; 也是只对比本身的元类, 不会沿着继承链向上查找; 在上层只有对象方法, 我认为就是源于类对象是元类的实例对象, 就是说类对象也是一个对象;

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

+ (BOOL)isKindOfClass:(Class)cls

这个方法实现其实与对象方法也类似, 只是对比的是元类及元类的继承链, 首先判断的是 self(调用者)本身的元类型与 cls 是否是同一个类, 是就返回YES, 不是就循环取出对应的父类的元类型继续比较, 一直沿着元类继承链向上找到根元类, 因为根元类的父类是 NSObject, 所以最后还是会找到 NSObject, 如果还不是就取 NSObject 的父类, 因为 NSObject 的父类指向的是 nil, 所以循环结束. 如果到循环结束都没有返回, 最后就返回 NO; 简单来说就是从本类的元类开始, 沿着元类的继承链向上找, 只要有一个元类(类)与 cls 是同一个类, 就返回 YES.

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

验证

创建两个类, 分别是 TeacherPerson, Teacher 继承自 Person, Person 继承自 NSObject. 用现在的示例去验证上面的分析;

验证对象调用

testInstanceMethods 的示例和打印结果

void testInstanceMethods(void) {
    
    Person *person = [Person alloc];
    Class pClass = [Person class];
    Class objCls = [NSObject class];
    
    BOOL re1 = [person isKindOfClass: pClass];
    BOOL re2 = [person isKindOfClass: objCls];
    BOOL re3 = [person isMemberOfClass: pClass];
    BOOL re4 = [person isMemberOfClass: objCls];
    // 打印结果: 1110
    NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
}

image.png

  • re1person 对象的类型就是 pClass, 所以返回 YES;
  • re2person 对象的类型不是 objCls, 但是沿着继承链向上最终会找到 NSObject, 所以最终也返回 YES
  • re3person 对象的类型就是 pClass, 所以返回 YES;
  • re4person 对象的类型显然不是 objCls, 也不会沿着继承链查找, 所以最终会返回 NO;

验证类调用

testClassMethods 的示例和打印结果

void testClassMethods(void)  {
    
    Class tClass = [Teacher class];
    Class objCls = [NSObject class];
    id pMetaClass = objc_getMetaClass("Person");
    id tMetaClass = objc_getMetaClass("Teacher");
    
    BOOL re1 = [tClass isKindOfClass: tClass];
    BOOL re2 = [tClass isKindOfClass: tMetaClass];
    BOOL re3 = [tClass isKindOfClass: pMetaClass];
    BOOL re4 = [tClass isKindOfClass: objCls];
    
    BOOL re5 = [tClass isMemberOfClass:tMetaClass];
    BOOL re6 = [tClass isMemberOfClass:pMetaClass];
    // 打印结果: 011110
    NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n re5 :%hhd\n re6 :%hhd\n",re1,re2,re3,re4,re5,re6);
}

image.png

  • re1tClass 类的元类型是 tMetaClass, 而不是 tClass 本身, 所以会沿着 tMetaClass 的继承链向上查找到 根元类, 因为 根元类 的父类是 NSObject, 所以最后会找到 NSObject, 整个继承链都不会和 tClass 不是同一个类, 所以最终返回 NO;
  • re2tClass 类的元类型就是 tMetaClass, 所以返回 YES;
  • re3tClass 类的元类型是 tMetaClass, 而不是 pMetaClass, 然后会沿着tMetaClass的继承链向上查找, 因为 tMetaClass 的父类是 pMetaClass, 所以会找到 pMetaClass, 此时判断条件成立, 所以返回 YES;
  • re4tClass 类的类型是 tMetaClass, 而不是 objCls, 所以会沿着 tMetaClass 的继承链向上查找到 根元类, 因为 根元类 的父类是 NSObject, 所以最后会找到 NSObject, 此时判断条件成立, 所以返回 YES;
  • re5tClass 类的元类型就是 tMetaClass, 所以返回 YES, 与 re2 情况相同, 只是不会循环查找;
  • re6tClass 类的元类型是 tMetaClass, 而不是 pMetaClass, 又不会沿着 tMetaClass 的继承链向上查找, 所以是直接返回 NO;

总结

类对象也是对象, 所以我们统一从对象的角度出发来总结. 只是在用的时候有类对象和元类这个概念. 心中不忘 isa 走位图, 这样我们就可以轻松应对;

- (BOOL)isKindOfClass:(Class)cls 首先对比对象本身的类是否就是传入的 cls, 如果不是就沿着类的继承链向上查找, 直到 NSObject, 一级一级对比, 如果遇到是传入的 cls, 就返回YES, 如果所有都不是传入的 cls, 那么最终返回 NO;

- (BOOL)isMemberOfClass:(Class)cls 直接对比对象本身的类是否就是传入的 cls, 如果是传入的 cls, 就返回YES, 如果不是传入的 cls, 就返回 NO, 不会沿类继承链向上查找;

相信结合 isa 走位图, 源码, 源码分析, 结论验证 这一系列骚操作之后, 你一定会对这两个方法有一个全新的认识, 终生难忘 😄😄😄😄😄😄

走过路过不要错过, 喜欢就点个赞吧, 谢谢了...