objc_msgsend(中)消息慢速查找

1,927 阅读3分钟

前言

在之前学习# IOS撸Runtime(上)我们了解到了objc_msgsend的快速查找流程, 获取class->内存偏移获取cache->获取buckets->内存偏移获取mask在高16位->异或sel得到index下标->buckets平移index*(2*16)大小找到对应bucket并且bucket--向前平移一个位置->取出相应的sel是否和消息接受者相等->如果相等走cachehit->不相等判断是否为0->为0走missLabeldaynamic->不为0循环走完->向前遍历 还没有找到走missLabeldaynamic,接下来我们就开始探索这个方法。

准备工作

objc_msgSend_uncached

CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached 中的第三个参数赋值给 .macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant. 全文搜索objc_msgSend_uncached

1.png

TailCallFunctionPointer

2.png

  • $0 = p17 但方法实现并没有操作imp的代码,猜想MethodTableLookup里面操作了imp并且赋值给p17

MethodTableLookup

3.png

  • bl:b:跳转 l:链接寄存器 在跳转到之前_lookUpImpOrForward之前

  • 将下一条指令的地址保存到lr寄存器中,既将(mov x17, x0)的指令地址保存在br中

  • _lookUpImpOrForwar执行完以后,执行br寄存器中的地址

lookUpImpOrForwar

1.png

lookUpImpOrForwar详细分析

2.png

realizeAndInitializeIfNeeded_locked分析

1.png

findMethodInSortedMethodList 二分查找流程

2.png

  • 二分查找算法运用到极致 first 方法数组的开始位置 probe遍历查找的目标,keyValue不变目标sel,count方法数组总个数。
  • probe-- 是当分类方法和主类方法同名时,先加载分类方法。下面有图解。

二分查找流程图解

2.png

二分查找流程伪代码

        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 --;
            }
        }

消息慢速查找流程图解

1.png

总结

慢速查找流程

  • 是否注册类,如果没有直接报错
  • 是否实现cls,如果没有实现,则先去实现以及相关的isa走位链isa继承链中类的实现,目的是方法查找时到父类中去查询
  • 类是否初始化,如果没有则初始化,这一步我觉着是创建类对象比如调用类方法时,就是类对象调用实例方法

cls开始遍历查询

  • 判断是否有共享缓存,目的是有可能在查过过程中这个方法被调用缓存了,如果有的话直接从缓存中取,没有共享缓存则开始到本类中查询
  • 在类中采用二分查找算法查找methodlist中的方法,如果找到插入缓存中,循环结束

父类缓存中查询

  • 如果父类中存在循环则终止查询,跳出循环
  • 此时curClass = superclass,到父类的缓存中找,如果找到则插入到本类的缓存中。如果父类中返回的是forward_imp则跳出遍历,执行消息转发
  • 如果本类中没有找到此时的curClass = superclass进入和cls类相同的查找流程进行遍历循环,直到 curClass = nilimp = forward_imp进行消息转发