前言
上一节我们已经通过汇编探索一下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掩码->bucket
5.mask掩码 -> mask
6.insert哈希函数(mask_t)(value & mask);
7.第一次查找index
8.bucket+index整个缓存里面的第几个bucket
9.bucket(imp sel)
10 拿到sel == __cmd ->cacheHit->imp^isa = imp(br)调用imp
没找到
11 拿到bucket–再次平移
12 直到死循环
13.如果一直没找到 __objc_msgSend_uncached
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END