前言
上一节我们已经通过汇编探索一下objc_msgSend走到了
CacheLookup 流程 下面我们研究一下CacheLookup
CacheLookup
NORMAL,_objc_msgSend ,__objc_msgSend_uncached,MissLabelDynamic,MissLabelConstant
源码分析:真机为例。
eor p12,p1,p1,LSR #7按位右移动7位如下图:
- 获取
_bucketsAndMaybeMask地址也就是cache的地址:p16 = isa(class),p16 + 0x10 = _bucketsAndMaybeMask = p11 - 获取
buckets地址就是缓存内存的首地址:buckets = ((_bucketsAndMaybeMask >> 48 )- 1 ) - 获取
hash下标:p12 =(cmd ^ ( _cmd >> 7))& msak这一步的作用就是获取hash下标index - 流程如下:
isa --> _bucketsAndMaybeMask --> buckets -->hash下标
遍历缓存
- 根据下标
index找到index对应的bucket。p13 = buckets + ((_cmd ^ (_cmd >> 7)) & mask) << (1+PTRSHIFT)) - 先获取对应的
bucket然后取出imp和sel存放到p17和p9,然后*bucket--向前移动 1流程:p9= sel和 传入的参数_cmd进行比较。如果相等走2流程,如果不相等走3流程2流程:缓存命中直接跳转CacheHit流程3流程:判断sel = 0条件是否成立。如果成立说明buckets里面没有传入的参数_cmd的缓存,没必要往下走直接跳转__objc_msgSend_uncached流程。如果sel != 0说明这个bucket被别的方法占用了。你去找下一个位置看看是不是你需要的。然后在判断下个位置的bucket和第一个bucket地址大小,如果大于第一个bucket的地址跳转1流程循环查找,如果小于等于则接继续后面的流程- 如果循环到
第1个bucket里都没有找到符合的_cmd。那么会接着往下走,因为下标index后面的可能还有bucket还没有查询
CacheHit流程
CacheHit \Mode的 Mode = NORMAL
TailCallCachedImp是一个宏,宏定义如下
缓存查询到以后直接对bucket的imp进行解码操作。即imp = imp ^ class,然后调用解码后的imp
mask向前遍历缓存
向前遍历缓存没有查询到就会跳转到mask对应的bucket继续向前查找
- 找到最后一个
bucket的位置:p13 = buckets + (mask << 1+3)找到最后一个bucket的位置 - 先获取对应的
bucket然后取出imp和sel存放到p17和p9,然后*bucket--向前移动 p9= sel和 传入的参数_cmd进行比较。如果相等走2流程- 如果不相等在判断(
sel != 0 && bucket > 第一次确定的hash下标bucket)接着循环缓存查找,如果整个流程循环完仍然没有查询到或者遇到空的bucket。说明该缓存中没有缓存)sel = _cmd的方法,缓存查询结束跳转__objc_msgSend_uncached流程 - mask向前遍历和前面的循环遍历逻辑基本一样
总结
objc_msgSend(recevier,_cmd) sel->imp1.
recevier是否存在2.
recevier -> isa -> class (GetClassFromIsa_p16)3.
class->内存平移 ->cache(bucket mask)4.
bucket掩码->bucket5.
mask掩码->mask6.
insert哈希函数(mask_t)(value & mask);7.第一次查找
index8.
bucket+index整个缓存里面的第几个bucket9.
bucket(imp sel)10 拿到
sel == __cmd ->cacheHit->imp^isa = imp(br)调用imp没找到
11 拿到
bucket--再次平移12 直到死循环
13.如果一直没找到
__objc_msgSend_uncached