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
是有系统版本要求的。