isKindOfClass & isMemberOfClass 的分析
测试代码
@interface MYObject : NSObject
@end
@implementation MYObject
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL r1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL r2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL r3 = [(id)[MYObject class] isKindOfClass:[MYObject class]];
BOOL r4 = [(id)[MYObject class] isMemberOfClass:[MYObject class]];
printf("%d - %d - %d - %d\n", r1, r2, r3, r4);
BOOL r5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL r6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL r7 = [(id)[MYObject alloc] isKindOfClass:[MYObject class]];
BOOL r8 = [(id)[MYObject alloc] isMemberOfClass:[MYObject class]];
printf("%d - %d - %d - %d\n", r5, r6, r7, r8);
}
return 0;
}
最终输出结果如下:
1 - 0 - 0 - 0
1 - 1 - 1 - 1
源码溯源
找出背后调用的方法
首先我们的第一反应就是 objc 源码中直接全局搜索 isKindOfClass:
,也并没有让我们失望,在 NSObject.mm
文件中找到了两个相关的方法:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
真想是否像我们想象的那样,即将浮出水面呢?让我们打个断点调试一下吧
让我们满怀期待的按下 Command + R 运行一下吧。
Oh my god! 什么鬼!我的断点怎么没进来呢???
让我们在 r1 这行代码中打个断点,然后查看下汇编代码:
原来眼前的一切皆是假象,背后调用的实际是 objc_opt_isKindOfClass
这个函数。
objc_opt_isKindOfClass
该函数的实现代码如下:
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
通过源码我们可以知道,除非 fastpath(!cls->hasCustomCore())
返回 false
或者在非 __OBJC2__
中才会走 isKindOfClass:
方法,这也就说明了为什么我们的断点没有进来的原因。
分析 isKindOfClass 执行过程
分析
BOOL r1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
:- 进入 objc_opt_isKindOfClass 方法
- otherClass 指向的是 NSObject 类
- cls 通过 isa 指向根元类, 所以循环体中的 tcls 第一次指向的就是根元类
- 根元类 ≠ 类,此时通过
getSuperclass
获取父类,根元类的父类就是 NSObject 类 - 所以 tcls 和 otherClass 相等,返回 YES
分析
BOOL r3 = [(id)[MYObject class] isKindOfClass:[MYObject class]];
:- 进入 objc_opt_isKindOfClass 方法
- otherClass 指向的是 MYObject 类
- cls 通过 isa 指向 MYObject 的元类, 所以循环体中的 tcls 第一次指向的就是 MYObject 元类
- 元类 ≠ 类,此时通过
getSuperclass
获取父类(元类的父类就是父类的元类),此时 tcls 指向 NSObject 的元类 - …
- 经过几轮比对,tcls 都不等于 otherClass,所以返回了 NO
分析
BOOL r7 = [(id)[MYObject alloc] isKindOfClass:[MYObject class]];
- 进入 objc_opt_isKindOfClass 方法
- otherClass 指向的是 MYObject 类
- 实例对象的 isa 指向类,所以此时的 cls 为 MYObject 类
- MYObject 类 == MYObject 类,返回 YES
isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
isMemberOfClass:
的分析参考 isKindOfClass:
的过程就行了,这里不再阐述;归根结底还是要理解好 isa 的流程图。
isa 流程图
请牢牢记住并充分理解 isa 流程图,这个是分析底层代码执行的前提。