前言
在之前学习# IOS撸Runtime(上)我们了解到了objc_msgsend的快速查找流程,
获取class->内存偏移获取cache->获取buckets->内存偏移获取mask在高16位->异或sel得到index下标->buckets平移index*(2*16)大小找到对应bucket并且bucket--向前平移一个位置->取出相应的sel是否和消息接受者相等->如果相等走cachehit->不相等判断是否为0->为0走missLabeldaynamic->不为0循环走完->向前遍历 还没有找到走missLabeldaynamic,接下来我们就开始探索这个方法。
准备工作
- objc4-818.2 源码
objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached 中的第三个参数赋值给
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant.
全文搜索objc_msgSend_uncached
TailCallFunctionPointer
$0 = p17但方法实现并没有操作imp的代码,猜想MethodTableLookup里面操作了imp并且赋值给p17
MethodTableLookup
-
bl:b:跳转 l:链接寄存器 在跳转到之前_lookUpImpOrForward之前
-
将下一条指令的地址保存到lr寄存器中,既将(mov x17, x0)的指令地址保存在br中
-
当
_lookUpImpOrForwar执行完以后,执行br寄存器中的地址
lookUpImpOrForwar
lookUpImpOrForwar详细分析
realizeAndInitializeIfNeeded_locked分析
findMethodInSortedMethodList 二分查找流程
- 二分查找算法运用到极致
first方法数组的开始位置probe遍历查找的目标,keyValue不变目标sel,count方法数组总个数。 probe--是当分类方法和主类方法同名时,先加载分类方法。下面有图解。
二分查找流程图解
二分查找流程伪代码
int state = 0;
int count ;//总方法数
int keyvalue = 45 ; //目标查找的sel
int probe ;
int base;
base = state;
for (count =1790; count!=0; count = count/2) {
probe = base+(count/2);
if (keyvalue == probe) {
return probe;
}
if (keyvalue > probe) {
base = probe +1;
count --;
}
}
消息慢速查找流程图解
总结
慢速查找流程
- 是否注册类,如果没有直接报错
- 是否实现
cls,如果没有实现,则先去实现类以及相关的isa走位链和isa继承链中类的实现,目的是方法查找时到父类中去查询 - 类是否初始化,如果没有则初始化,这一步我觉着是创建类对象比如调用类方法时,就是类对象调用实例方法
cls开始遍历查询
- 判断是否有共享缓存,目的是有可能在查过过程中这个方法被调用缓存了,如果有的话直接从缓存中取,没有共享缓存则开始到本类中查询
- 在类中采用二分查找算法查找
methodlist中的方法,如果找到插入缓存中,循环结束
父类缓存中查询
- 如果父类中存在循环则终止查询,跳出循环
- 此时
curClass=superclass,到父类的缓存中找,如果找到则插入到本类的缓存中。如果父类中返回的是forward_imp则跳出遍历,执行消息转发 - 如果本类中没有找到此时的
curClass=superclass进入和cls类相同的查找流程进行遍历循环,直到curClass=nil,imp=forward_imp进行消息转发