阅读 273

iOS objc_msgSend 慢速查找流程

封面图11.jpg

开篇

好好学习,不急不躁。

上篇文章中,主要研究了objc_msgSend的消息发送流程,分析的场景主要是在msgSend发送顺畅,缓存命中的情况下;那在缓存未命中的情况下,消息发送流程有是如何执行的呢?今天我们就来探析一下;

方法缓存

objc_msgSend 的消息发送过程中,如果要查找的sel,与查询到的sel 相同,则代表缓存命中,执行CacheHit函数逻辑;如果缓存未命中,则执行MissLabelDynamic函数逻辑;

慢速查找.png

从CacheLookup 传递的值可以看出对应的函数:

Mode              ----> NORAML;

Function          ----> _objc_msgSend;

MissLabelDynamic  ----> _objc_msgSend_uncached;

既然MissLabelDynamic函数,执行的是_objc_msgSend_uncached流程,那么接下来,就主要分析_objc_msgSend_uncached流程

msgSend 缓存未命中

.endmacro
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves

MethodTableLookup

TailCallFunctionPointer x17

......

复制代码

在源码内,MethodTableLookupTailCallFunctionPointer x17这两个函数比较重要;

慢速查找-第 3 页.png

TailCallFunctionPointer函数,做了数据返回,也表示在它之前数据获取已基本完毕,所以我们重点看MethodTableLookup函数;

慢速查找-第 4 页.png

通过逐层分析,最终得出,_lookUpImpOrForward函数是一切的关键;

lookUpImpOrForward

全局搜索 _lookUpImpOrForward 函数,却发现在汇编的源码中,并没有_lookUpImpOrForward的源码,表明_lookUpImpOrForward函数,并不是使用汇编源码流程,尝试搜索lookUpImpOrForward,却发现lookUpImpOrForward流程,是以C++的源码形式存在;

那么问题来了,为什么缓存命中的时候,采用汇编方式,而缓存未命中时,执行的却是C++的方式?采用汇编有什么好处呢?

汇编是机器识别语言,对于设备来说,识别汇编语言执行起来是非常高效的,并且是非常安全的;

所以在缓存命中的时候,通过缓存直接查找方式是非常快速的,也就是我们通常说的,快速查找流程;

而在缓存未命中的时候,需要遍历所有方法查找,这个过程就比较缓慢,所以是一个慢速查找流程。系统就将该流程放在c/c++里;

慢速查找

慢速查找流程

1、首先查找当前类的method list,看看有没有当前sel对应的imp;

2、子类没有,再继续查找父类 method list;

3、父类没有,查找NSObject method list;

4、最后走到nil,跳出查找流程;


lookUpImpOrForward方法中,我们的目标就是获取imp,所以我们针对核心区代码进行解读;

慢速查找-第 7 页.png

method 二分法

慢速查找-第 8 页.png

深入查看getMethodNoSuper_nolock方法,是如何进行二分法查找的。

慢速查找-第 9 页.png

而在getMethodNoSuper_nolock并没有二分查找的痕迹,这是因为method list可能是一个二维数组,这个我们在之前讲解类方法获取的文章中讲解过;既然 method list 可能是二维数组,那么就无法在此处做二分法;

search_method_list_inline 获取的是 method_t,而search_method_list_inline内部依据判断条件,执行findMethodInSortedMethodList函数,那么我们着重分析这个函数;

findMethodInSortedMethodList函数

慢速查找-第 10 页.png

源码内的二分法非常精妙,通过两次的右移与减减操作,能精确定位二分下标,同时也减少循环操作,提升效率;

举个例子说明此处的二分法:

慢速查找-第 11 页.png

method 查找结束后,通过method即可获取imp,接着执行 goto done方法,进行下一步;

done方法内部,将会进行缓存写入,防止下一次再次获取时,再次执行二分法查找消耗内存,存入缓存同时也提高效率;

慢速查找-第 12 页.png

至此,消息的发送、查找,插入就形成一个闭环;

消息慢速查找流程,会通过继承链不断的查找,子类没有,查询父类,父类的查询流程,也一样存在快速查找与慢速查找;

慢速查找-第 13 页.png

总结

慢速查找-第 14 页.png

文章分类
iOS