前言
对于iOS开发者而言,isKindOfClass:
与isMemberOfClass:
应该是相当熟悉的,今天我们不是要讲这两个方法的用法,而是讨论一个关于这两个方法的面试题。
正文
大家思考一下下面这个面试题:
BOOL result1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL result2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL result3 = [(id)[PYTeacher class] isKindOfClass:[PYTeacher class]];
BOOL result4 = [(id)[PYTeacher class] isMemberOfClass:[PYTeacher class]];
其实分析这个面试题,只要根据 isa
的指向图,加以分析就能得出正确的结论:
result1 = YES;
result2 = NO;
result3 = NO;
result4 = NO;
分析过程:
NSObject类对象
属于 NSObject元类
,NSObject元类
的 父类
是 NSObject类
,所以result1 = YES
、result2 = NO
。
同理:
PYTeacher类对象
属于 PYTeacher元类
,但是在 PYTeacher元类
的 继承链
中不包含 PYTeacher类
,所以result3 = NO
、result4 = NO
。
小结
本来分析到这里,这篇文章也就该结束了。但是,笔者曾亲身经历过这个面试题,也是这样分析的,但面试官始终追问一句:你有没有看过 isKindOfClass: 的实现?
今天,同样把这个问题抛给大家:你有没有看过 isKindOfClass: 的实现?
isKindOfClass:
我们可以通过 objc
的源码查看 isKindOfClass:
的具体实现:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
so easy!
就这么简单!一个 for
循环而已!
让我们运行一下,在isKindOfClass:
里打个断点:
这时,意外发生了!!!
程序并没有停在断点这里!!!
什么情况?方法为什么没有执行?
这个方法没有执行,肯定是出了什么问题,那能是什么问题呢?我们只是运行了一下代码而已,难道是编译器做了手脚?
LLVM
刚才我们想到了编译器,那我们就从 LLVM
里面寻找答案。我们可以在LLVM
的代码里搜索isKindOfClass
,会找到如下内容:
// This is the table of ObjC "accelerated dispatch" functions. They are a set
// of objc methods that are "seldom overridden" and so the compiler replaces the
// objc_msgSend with a call to one of the dispatch functions. That will check
// whether the method has been overridden, and directly call the Foundation
// implementation if not.
// This table is supposed to be complete. If ones get added in the future, we
// will have to add them to the table.
const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
"objc_alloc",
"objc_autorelease",
"objc_release",
"objc_retain",
"objc_alloc_init",
"objc_allocWithZone",
"objc_opt_class",
"objc_opt_isKindOfClass",
"objc_opt_new",
"objc_opt_respondsToSelector",
"objc_opt_self",
};
注释的大体意思就是,这是个加速调度的函数表,这里面是一些很少被覆盖的objc
的方法,所以编译器会用他们替换objc_msgSend
。我们发现其中有一个objc_opt_isKindOfClass
,我们猜测编译器会用他来替换isKindOfClass:
方法。我们在 objc
的源码中搜索这个方法,并打一个断点。
如图2中,果然像我们猜测的那样,调用了
objc_opt_isKindOfClass
方法,而这个方法的主要内容也是一个 for
循环,至此 isKindOfClass:
我们就分析完了。
isMemberOfClass
isMemberOfClass
的源码如下:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
isMemberOfClass
的源码并不复杂,这里就不再赘述了。
总结
当我们在调用isKindOfClass:
方法的时候,编译器已经把方法替换了,实际运行的时候,会调用 objc_opt_isKindOfClass
方法,方法内部会通过一个 for
循环来追溯,对象所属的类在不在目标类的继承链中。