ios 底层相关 isKindOfClass isMemberOfClass的面试题

面试题

话不多说,通过一个面试题来探究下isKindOfClassisMemberOfClass

  BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
    BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
    BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson 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)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
    BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

复制代码

这段代码运行的结果是多少呢

结果

2021-07-09 16:22:06.480627+0800 KCObjcBuild[74708:6582366]  re1 :1
 re2 :0
 re3 :0
 re4 :0
2021-07-09 16:22:06.481524+0800 KCObjcBuild[74708:6582366]  re5 :1
 re6 :1
 re7 :1
 re8 :1
复制代码

分析

为什么结果是这样的呢 继续分析,打开 debug->debug workflow->always show disassembly 允许汇编调试

Xcode中把macOS的版本调到10.15以下,或者iOS的版本13.0以下。查看下汇编代码图片[1]-ios 底层相关 isKindOfClass isMemberOfClass的面试题-一一网
源码分析:isKindOfClassisMemberOfClass底层的实现都是objc_msgSend消息转发。通过SEL找到对应的IMP

Xcode中把macOS的版本调到10.15以上,或者iOS的版本13.0以上。查看下汇编代码图片[2]-ios 底层相关 isKindOfClass isMemberOfClass的面试题-一一网
源码分析:isKindOfClass底层的实现objc_opt_isKindOfClass,class底层的实现objc_opt_class,isMemberOfClass还是走消息转发

isKindOfClass 底层实现

//类方法  对应re1 跟 re3
+ (BOOL)isKindOfClass:(Class)cls {
//tcls = 类的元类self->ISA()
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
    // 类的元类tcls vs  cls传入的需要比较的类
        if (tcls == cls) return YES;
    }
    return NO;
}
//对象方法  对应re5 跟 re7
- (BOOL)isKindOfClass:(Class)cls {
//tcls = 类 
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
     // 类tcls vs  cls传入的需要比较的类
        if (tcls == cls) return YES;
    }
    return NO;
}
复制代码

分析:

  • +isKindOfClass 流程。 类的元类 vs cls(需要比较的类),不同继续比较 。元类的父类 vs cls,不同继续比较直到找到根元类根元类 vs cls,不同继续比较。根类(NSObject) vs cls,如果还不相同则根类(NSObject)的父类nil,跳出循环返回NO
  • -isKindOfClass 流程。获取当前对象所属类 vs cls,不同继续比较 。类的父类 vs cls,不同继续比较直到找到根类(NSObjec)根类(NSObject) vs cls,如果还不相同则根类(NSObject)的父类nil,跳出循环返回NO

isMemberOfClass底层实现

//类方法  对应re2 跟 re4
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
//对象方法  对应re6 跟 re8
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
复制代码

分析:

  • +isMemberOfClass 流程。 类的元类 vs cls(需要比较的类),相同就返回YES,否则返回NO

  • -isMemberOfClass 流程。 vs cls(需要比较的类),相同就返回YES,否则返回NO

objc_opt_isKindOfClass 底层实现

objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
	OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
        
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__ //现在基本上都用OBJC2版本
    //slowpath(!obj)  obj为空的是小概率事件基本不会发生
    if (slowpath(!obj)) return NO;
    // 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
    Class cls = obj->getIsa();
    //fastpath(!cls->hasCustomCore()) (类或者父类中大概率没有默认的isKindOfClass方法)
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif //OBJC版本直接走消息转发
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
复制代码

分析
基本上都用OBJC2版本,obj->getIsa()获取或者元类:obj是对象获取类,obj是获取元类 然后就接着for循环,for里面的代码和isKindOfClass逻辑一样

objc_opt_Class 底层实现

OBJC_EXPORT Class _Nullable
objc_opt_class(id _Nullable obj)
   OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
// Calls [obj class]
Class
objc_opt_class(id obj)
{
#if __OBJC2__
   if (slowpath(!obj)) return nil;
   // 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
   Class cls = obj->getIsa();
   //(类或者父类中大概率没有默认的class方法)
   if (fastpath(!cls->hasCustomCore())) {
       //cls是类 返回cls,如果cls是元类,obj是类,返回obj还是类
       return cls->isMetaClass() ? obj : cls;
   }
#endif
   return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
}
   
复制代码

分析:
objc_opt_class 的实现,其实就是获取类,如果参数是对象则返回,如果是返回类

验证结果

  • reg1:+isKindOfClass 比较的是 (NSObject的元类)NSObject vs NSObject,返回 1
  • reg2:+isMemberOfClass 比较的是 NSObject的元类根元类 vs NSObject,返回 0
  • reg3:+isKindOfClass 比较的是 LGPerson的元类NSObject vs LGPerson,返回 0
  • reg4: +isMemberOfClass比较的是 LGPerson的元类NSObject vs LGPerson,返回 0
  • reg5: -isKindOfClass比较的是 NSObject对象所属类NSObject vs NSObject,返回 1
  • reg6: -isMemberOfClass 比较的是 NSObject类 vs NSObject,返回 1
  • reg7: -isKindOfClass 比较的是 LGPerson对象所属类LGPerson vs LGPerson,返回 1
  • reg8: -isMemberOfClass 比较的是 LGPerson类 vs LGPerson,返回 1

总结

  • + isKindOfClass方法:元类-->元类的父类-->直到找到根元类cls分别进行比较

  • - isKindOfClass方法:类-->类的父类-->直到找到根类(NSObject)cls分别进行比较

  • + isMemberOfClass方法:元类 vs cls

  • - isMemberOfClass方法: vs cls

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享