本页所使用的objc runtime 756.2,来自GITHUB
1. 概念
在前文中,已经总结了方法查找的流程,今天从代码层面上继续阐述。
isa 的指向图如下所示:
2. 方法查找流程
2.1 从业务代码分析
配置代码环境:,先从子类父类方法查找说起,先创建几个类,有:
父类 Person 继承自NSObject,有方法
talk
子类 Student 继承自 Person,有方法
sayEnglish
扩展 NSObject + sayGerman,有方法
sayGerman
具体代码如下所示
主业务代码图:
由上图可以看出,我们生成的子类Student
对象,既可以执行自己的对象方法sayEnglish
,也可以执行父类方法talk
,当需要执行的方法——子类和父类都没有之后,也可以执行根类扩展方法sayGerman
。
查找逻辑图可以初步理解为:子类——> 父类——> 父类的父类 ——>根元类(NSObject)
类方法也是类似的,就不赘述
2.2 源码分析
2.2.1 class_lookupMethodAndLoadCache3 - 慢速查找开启
/*********************************************************************** |
原来上文留下的class_lookupMethodAndLoadCache3 方法,最终执行的方法是lookUpImpOrForward,继续探究一下!
lookUpImpOrForward - 查找Imp 或转发消息
乐观检查是否有缓存
// Optimistic cache lookup |
查找类是否缓存过
// runtimeLock is held during isRealized and isInitialized checking |
这里通过runtimeLock
锁住该部分内存,进行查找,执行了isKnownClass
的方法,具体实现在这里
/*********************************************************************** |
在类的缓存(cache_t)找
// Try this class's cache. |
在类的方法列表里寻找
// Try this class's method lists. |
在父类的缓存和方法列表寻找
// Try superclass caches and method lists. |
父类缓存(Cache)找
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
父类方法列表找
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
有可能内存覆盖,再给一次查找
这里的核心方法是
// No implementation found. Try method resolver once. |
父类也没有——查找失败——报错
// No implementation found, and method resolver didn't help. |
但是这个_objc_msgForward_impcache
的实现可不好找,在源码里只看到了这些:
|
最终通过搜索查找**_objc_msgForward_impcache**,在汇编源码找到了类似的代码
得到这个叫做__objc_forward_handler 的代码块,搜索得知它继承自objc_defaultForwardHandler,
继续查找,得到最终的源码如下:
OH MY GOD! 原来这就是传说中——让程序员捶胸顿足的方法查找失败的代码
至此,方法查找的流程已经捋完。
3. 小结
方法查找的流程,就是在类里查找缓存与方法列表里挖掘的过程。
如上文阐述:
方法查找:缓存查找 —— 类的方法列表 —— 父类的方法列表 (递归)—— 动态方法解析 —— 结束