【iOS】——探究isKindOfClass和isMemberOfClass底层实现

诗林  金牌会员 | 2024-8-10 17:19:03 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 485|帖子 485|积分 1455

isKindOfClass

判断该对象是否为传入的类或其子类的实例
  1. // 类方法实现,用于检查一个类是否属于另一个类或其父类链上的任何类。
  2. + (BOOL)isKindOfClass:(Class)cls {
  3.     // 从当前类开始,tcls将沿着元类的继承链向上遍历。
  4.     for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
  5.         // 检查当前类tcls是否等于要检查的类cls。
  6.         if (tcls == cls) return YES; // 如果相等,立即返回YES,表示属于该类或其子类。
  7.     }
  8.     // 如果遍历完整个继承链都没有找到匹配的类,返回NO。
  9.     return NO;
  10. }
  11. // 实例方法实现,用于检查一个对象是否属于指定的类或其任何父类。
  12. - (BOOL)isKindOfClass:(Class)cls {
  13.     // 从对象的类开始,tcls将沿着继承链向上遍历。
  14.     for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
  15.         // 检查当前类tcls是否等于要检查的类cls。
  16.         if (tcls == cls) return YES; // 如果相等,立即返回YES,表示属于该类或其子类。
  17.     }
  18.     // 如果遍历完整个继承链都没有找到匹配的类,返回NO。
  19.     return NO;
  20. }
复制代码
isKindOfClass分为类方法实例方法雷同点都是起首判断调用者的isa指针指向的对象是否和传入的cls对象雷同,如果不雷同则沿着继续链获取调用者的父类的isa指针接着判断其指向的对象和传入的cls对象雷同。
如果雷同则返回YES,否则一直沿着继续链找直到tcls为nil退出循环并返回NO。至于tcls为什么能为nil呢,因为任何OC对象沿着继续链向上都会到根类NSObject类而NSObject类的Superclass为nil
说完了雷同点下面说下不同点,在OC中实例对象的isa指针指向它所属的类,类对象的isa指针指向它所属的元类。类有类的继续链,元类有元类的继续链,因此会走两条不同的路,但末了又会汇入到一块也就是根类NSObject类。
下面这张图是类和元类的继续链:
class是类,meta是元类。
虚线是isa指针,实线是父类指针。
不难发现类的isa指针指向所属的元类,元类沿着继续链到根元类而根元类的父类是根类(NSObject)

下面给出isKindOfClass流程图:
类方法调用流程

实例方法调用流程

总结一下
类对象调用isKindOfClass方法
   按照该类所属的元类 --> 根元类 --> 根类 --> nil 与 传入类的对比。
  实例对象调用isKindOfClass方法
   该对象所属的类 --> 父类 --> 根类 --> nil 与 传入类的对比。
  isMemberOfClass

判断该对象是否为传入的类的实例
  1. + (BOOL)isMemberOfClass:(Class)cls {
  2.     return self->ISA() == cls;
  3. }
  4. - (BOOL)isMemberOfClass:(Class)cls {
  5.     return [self class] == cls;
  6. }
复制代码
isMemberOfClass同样也是分为类方法和实例方法。
通过代码不难发现
类方法是判断类对象的isa指针指向的元类对象是否和传入的对象雷同
实例方法是判断通过当前对象(self)调用class方法([self class])返回的对象是否和传入的对象雷同
这里的class方法我们看下代码:
  1. // 类方法,返回自身
  2. + (Class)class {
  3.     return self;
  4. }
  5. // 实例方法,查找isa(类)
  6. - (Class)class {
  7.     return object_getClass(self);
  8. }
复制代码
实例对象调用的class方法又调用类object_getClass并将自身作为参数传入,接着进入object_getClass方法
  1. Class object_getClass(id obj)
  2. {
  3.     if (obj) return obj->getIsa();
  4.     else return Nil;
  5. }
复制代码
这里又调用了getIsa()方法
  1. inline Class
  2. objc_object::getIsa()
  3. {
  4.     if (!isTaggedPointer()) return ISA();
  5.     uintptr_t ptr = (uintptr_t)this;
  6.     if (isExtTaggedPointer()) {
  7.         uintptr_t slot =
  8.             (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
  9.         return objc_tag_ext_classes[slot];
  10.     } else {
  11.         uintptr_t slot =
  12.             (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
  13.         return objc_tag_classes[slot];
  14.     }
  15. }
复制代码
这个方法先判断对象是不是TaggedPointer类型,这里涉及到了指针优化的内容,末了又调用了ISA()方法
这里涉及到类与对象底层了,这里就不展开了,总之就是返回到该对象所属的类。
总结一下
类对象调用isMemberOfClass方法
   按照该类所属的元类 与 传入的类对比
  实例对象调用isMemberOfClass方法
   按照该对象所属的类 与 传入的类对比
  objc_opt-isKindOfClass

  1. - (void)viewDidLoad {
  2.     [super viewDidLoad];
  3.     // Do any additional setup after loading the view.
  4.     NSLog(@"rel = %d",[[NSObject class] isKindOfClass:[NSObject class]]);
  5. }
复制代码
运行下面代码并在
NSLog(@"rel = %d",[[NSObject class] isKindOfClass:[NSObject class]]);处加断点,打开汇编调试:Xcode -> Debug -> Debug Workflow -> Always show disassembly。 运行代码可以看到:

底层调用的不是isKindOfClass方法而是objc_opt-isKindOfClass方法,下面我们看下源码:
  1. BOOL objc_opt_isKindOfClass(id obj, Class otherClass) {
  2. #if __OBJC2__ // 如果是Objective-C 2.0版本及以上
  3.     if (slowpath(!obj)) return NO; // 慢路径检查,如果对象obj是nil,则直接返回NO
  4.     Class cls = obj->getIsa(); // 快速获取对象的类信息,ISA指向对象所属的类
  5.     if (fastpath(!cls->hasCustomCore())) { // 快路径检查,如果类没有自定义的核心实现
  6.         // 遍历类的继承链,检查是否包含otherClass
  7.         for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
  8.             if (tcls == otherClass) return YES; // 如果在继承链中找到了otherClass,返回YES
  9.         }
  10.         return NO; // 如果遍历完整个继承链都没有找到otherClass,返回NO
  11.     }
  12. #endif // 结束Objective-C 2.0及以上的条件编译
  13.     // 如果类有自定义的核心实现,或者不满足前面的快路径条件,
  14.     // 则通过消息发送的方式调用isKindOfClass:方法
  15.     return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
  16. }
复制代码
objc_opt-isKindOfClass是对isKindOfClass的方法对优化,起首会慢路径检查判断对象是否存在。接着获取对象所属的类,举行快路径检查判断类有没有自界说核心实现,接着遍历类的继续链并和传入的otherclass做比力。如果类有自界说的核心实现,或者不满意前面的快路径条件,则调用isKindOfClass方法
关于fastpath和slowpath
  1. //x很可能为真, fastpath 可以简称为 真值判断
  2. #define fastpath(x) (__builtin_expect(bool(x), 1))
  3. //x很可能为假,slowpath 可以简称为 假值判断
  4. #define slowpath(x) (__builtin_expect(bool(x), 0))
复制代码
__builtin_expect 指令是由 gcc 引入的
目的:编译器可以对代码举行优化,以减少指令跳转带来的性能下降。即性能优化
作用:允许程序员将最有可能实行的分支告诉编译器。
指令的写法为:__builtin_expect(EXP, N) 。表示 EXP==N的概率很大。
fastpath 界说中 __builtin_expect((x),1) 表示 x 的值为真的可能性更大;即 实行if 里面语句的时机更大
slowpath 界说中的 __builtin_expect((x),0) 表示 x 的值为假的可能性更大。即实行 else 里面语句的时机更大
在日常的开辟中,也可以通过设置来优化编译器,到达性能优化的目的,设置的路径为:Build Setting --> Optimization Level --> Debug --> 将None 改为 fastest 或者 smallest



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表