isKindOfClass和isMemberOfClass面试题
我们首先来看一段面试题代码,大家可以想一下re1-re8分别输出什么
在这之前我们需要了解isKindOfClass 和 isMemberOfClass
isKindOfClass:
returns YES if the receiver is an instance of the specified class or an instance of any class that inherits from the specified class.
方法调用者是传入的类的实例对象,或者调用者是传入类的继承者链中的类的实例对象,则返回YES。
isMemberOfClass:
returns YES if the receiver is an instance of the specified class.
方法调用者必须是传入的类的实例对象才返回YES。
这里是输出结果:
那么我们就上面的输出结果结合objc源码来分析一下,找到isKindOfClass和isMemberOfClass在源码中的方法实现:
我们发现了类方法和对象方法实现。平时开发过程中只会接触到对象方法的isKindOfClass和isMemberOfClass,但是在NSObject类中还隐式的实现了类方法版本。不只这两个方法,其他NSObject中的对象方法,都有其对应的类方法版本。因为在OC中,类和元类也都是对象。
isKindOfClass类方法
在for循环中的第一个代码块为Class tcls = self->ISA()即找到self的元类;
第二个代码块判断tcls是否存在,(注意,这边存在即直接最后的比较判断,然后再走地第三代码块);
第三个代码块为tcls = tcls->getSuperclass()即找到tcls的父类;
最后的判断if (tcls == cls)
-
re1是比较(id)[NSObject class]和[NSObject class],我们看isKindOfClass类方法,传进去的cls是NSObject,很明显,cls为NSObject,tcls也是NSObject,所以返回为YES,即1. -
re3是比较(id)[LhkhPerson class]和[LhkhPerson class],我们继续看isKindOfClass的类方法,传进去的cls是LhkhPerson,for循环里面首先拿到LhkhPerson的元类(这里的self就是LhkhPerson),即根元类NSObject(注意这边是一个for循环,后面还是会继续取NSObject父类即nil作比较的),可以得出cls=LhkhPerson肯定和tcls=NSObject,nil不一样,即返回的为NO,也就是0。
isKindOfClass对象方法
在for循环中的第一个代码块为Class tcls = [self class]即找到self的类;
第二个代码块判断tcls是否存在,(注意,这边存在即直接最后的比较判断,然后再走地第三代码块);
第三个代码块为tcls = tcls->getSuperclass()即找到tcls的父类;
最后的判断if (tcls == cls)
-
re5是比较(id)[NSObject alloc]和[NSObject class],那我们需要看isKindOfClass对象方法,传进去的cls是NSObject,而我们知道[self class](这里的self是NSObject对象)即NSObject,很明显,cls为NSObject,tcls也是NSObject,所以返回为YES,即1. -
re7是比较(id)[LhkhPerson alloc]和[LhkhPerson class],我们继续看isKindOfClass的类方法,传进去的cls是LhkhPerson,而我们知[self class](这里的self就是LhkhPerson对象)即LhkhPerson,很明显,cls为LhkhPerson,tcls也是LhkhPerson,所以返回为YES,即1.
isMemberOfClass类方法
return self->ISA() == cls;
直接返回self的元类和传进来的cls作比较
-
re2是比较(id)[NSObject class]和[NSObject class],我们看isMemberOfClass的类方法,self->ISA()(self为NSObject)即根元类NSObject,很显然类和根元类肯定是不能比较的,所以返回为NO,即0 -
re4是比较(id)[LhkhPerson class]和[LhkhPerson class],同理得出返回为NO,即0
isMemberOfClass对象方法
return [self class] == cls;
直接返回self的类和传进来的cls作比较
-
re6是比较(id)[NSObject alloc]和[NSObject class],我们看isMemberOfClass的兑现方法,[self class](self为NSObject对象)即类NSObject,很显然都是NSObject,所以返回为YES,即1 -
re8是比较(id)[LhkhPerson alloc]和[LhkhPerson class],同理得出返回为YES,即1
总结
这个面试题归根结底还是在考察我们对isa走向图和类的继承,也就是苹果官方这幅图:
补充
我们只是就底层源码对当前输出做了个解释,那么当我们给isKindOfClass添加断点时你会惊奇的发现压根就没来啊
我们打开汇编调试可以发现:
objc_opt_class,
objc_opt_isKindOfClass这两个又是些什么啊?我们通过符号埋点得到了这两个方法的实现:
我们现在使用的objc2,所以直接看objc2判断里面的代码,而objc_opt_class这个方法最终也是返回一个Class,也就是取决于obj,通过下面这行代码:
Class cls = obj->getIsa()
obj是对象,那么就取类;obj是类,那么就取元类;
然后就会进入到objc_opt_isKindOfClass这个方法的for循环中,其实就是上面的isKindOfClass相同。
总结
isKindOfClass出现无法进入断点的究其原因就是系统在编译时将isKindOfClass重定向到objc_opt_isKindOfClass这个方法中了,但是有个注意点,objc_opt_class,objc_opt_isKindOfClass是有系统版本要求的。