OC底层探索(七):isKindofClass & isMemberOfClass

问题

已知 WLWPerson : NSObject, 以下打印是什么

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
    BOOL re3 = [(id)[WLWPerson class] isKindOfClass:[WLWPerson class]];       //
    BOOL re4 = [(id)[WLWPerson class] isMemberOfClass:[WLWPerson class]];     //
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
    BOOL re7 = [(id)[WLWPerson alloc] isKindOfClass:[WLWPerson class]];       //
    BOOL re8 = [(id)[WLWPerson alloc] isMemberOfClass:[WLWPerson class]];     //
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
复制代码

打印结果

2021-07-06 15:49:48.316771+0800 KCObjcBuild[16324:1396474]  
 re1 :1
 re2 :0
 re3 :0
 re4 :0
2021-07-06 15:49:48.319309+0800 KCObjcBuild[16324:1396474]  
 re5 :1
 re6 :1
 re7 :1
 re8 :1
复制代码

继承与ISA流程图(重点)

isa流程图.png

isKindOfClass分析

具体调用的函数

要想搞清楚这个问题,我们首先要知道isKindOfClass的底层调用了什么函数,这个可以通过汇编查看具体的调用函数,不要直接在源码里去查找,因为有可能在编译阶段llvm对其做了中间层的处理,就像调用+ alloc,实际先调用的是objc_alloc

image.png
可以看出isKindOfClass 实际调用的是 objc_opt_isKindOfClass

  • 查看llvm源码发现,一个优先调用的表,the table of ObjC "accelerated dispatch" functions

image.png

  • 因为这些方法很少会被重写,因此编译器会替换调用objc_msgSend函数这将检查该方法是否被覆盖,并直接调用该函数

判断的逻辑

  • 再搞清楚具体的判断逻辑是什么,在源码中找到该函数objc_opt_isKindOfClass

image.png

  • 注释也有表明,调用isKindOfClass会走这里
  • 肯定是__OBJC2__,最近的版本都会走这里
  • Class cls = obj->getIsa()
    • obj 是 实例对象,如:[NSObject alloc]则返回该
    • obj 是 ,例如:[NSObjct Class], 则返回元类
  • fastpath(!cls->hasCustomCore()) = true

image.png
image.png
是否有默认的 new/self/class/respondsToSelector/isKindOf,我们都知道继承NSObject肯定有这些方法,所以是两次取反正好是true

  • Class tcls = cls; tcls; tcls = tcls->getSuperclass(), 拿到父类
  • tcls == otherClassself.classself.superClaee与其比较, 直到nil,有相同则是 true,没有反之
  • ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass), 如果不走上面的逻辑,则会通过objc_msgSend发送消息,调用isKindOfClass, 但目前的版本,不会走到这里

总结

  • 如果是实例对象 则用自己的类或自己的父类比较
  • 如果是类,则用元类或元类的父类比较

解析

 BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
复制代码
  • [NSObject class]根类, 所以是拿根元类或根元类的父类根类比较
  • 根元类的父类根类,所以rel1 = 1
 BOOL re3 = [(id)[WLWPerson class] isKindOfClass:[WLWPerson class]];
复制代码
  • [WLWPerson class]WLWPerson 类,所以是拿 MetaWLWPersonMetaWLWPerson的父类 与 WLWPerson 类 比较
  • 首先MetaWLWPerson != WLWPerson
  • MetaWLWPerson 的父类MetaNSObject, MetaNSObject != WLWPerson
  • MetaNSObject 的父类是 NSObject, NSObject != WLWPerson, 所以re3 = 0
 BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
复制代码
  • [NSObject alloc] 是实例对象,所以是拿NSObjectNSObject的父类与NSObject 比较
  • 显然 NSObject = NSObject, 所以re5 = 1
BOOL re7 = [(id)[WLWPerson alloc] isKindOfClass:[WLWPerson class]];
复制代码
  • [WLWPerson alloc] 是实例对象,所以是拿WLWPersonWLWPerson的父类与NSObject比较
  • WLWPerson的父类 是 NSObject, NSObject = NSObject, 所以re7 = 1

isMemberOfClass分析

底层调用的方法

image.png

  • 如果是类方法就是拿self -> ISA()也就是元类与其比较
  • 实例方法[self class]也就对象的类与其比较

解析

BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
复制代码
  • [NSObject class] 调用类方法,所以是拿 根元类MeataRootNSObject 与 根类 NSObject 比较
  • 显然 MeataRootNSObject != NSObject, 所以re2 = 0
BOOL re4 = [(id)[WLWPerson class] isMemberOfClass:[WLWPerson class]]; 
复制代码
  • [WLWPerson class] 调用类方法, 所以MetaWLWPersonWLWPerson 比较
  • MetaWLWPerson != WLWPerson, 所以re4 = 0
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
 
BOOL re8 = [(id)[WLWPerson alloc] isMemberOfClass:[WLWPerson class]];
复制代码
  • 都是调用实例方法,所以是拿 对象的类与 值比较,显然都是同一个类
  • 所以 re6 = re8 = 1
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享