OC底层原理08- objc_msgSend下

370 阅读1分钟

前言

上一节我们已经通过汇编探索一下objc_msgSend走到了 CacheLookup 流程 下面我们研究一下CacheLookup

CacheLookup

NORMAL,_objc_msgSend ,__objc_msgSend_uncached,MissLabelDynamic,MissLabelConstant

Xnip2021-06-29_21-43-01.jpg

源码分析:真机为例。

  • eor p12,p1,p1,LSR #7 按位右移动7位 如下图:

Xnip2021-07-01_10-48-28.jpg

Xnip2021-07-01_10-49-17.jpg Xnip2021-07-01_10-49-54.jpg

  • 获取_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下标

遍历缓存

Xnip2021-06-29_21-57-46.jpg

  • 根据下标index 找到index对应的bucketp13 = buckets + ((_cmd ^ (_cmd >> 7)) & mask) << (1+PTRSHIFT))
  • 先获取对应的bucket然后取出impsel存放到p17p9,然后*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

Xnip2021-06-29_22-00-30.jpg TailCallCachedImp是一个宏,宏定义如下

Xnip2021-06-29_22-02-20.jpg

缓存查询到以后直接对bucketimp进行解码操作。即imp = imp ^ class,然后调用解码后的imp

mask向前遍历缓存

向前遍历缓存没有查询到就会跳转到mask对应的bucket继续向前查找

Xnip2021-06-29_22-17-02.jpg

  • 找到最后一个bucket的位置:p13 = buckets + (mask << 1+3) 找到最后一个bucket的位置
  • 先获取对应的bucket然后取出impsel存放到p17p9,然后*bucket--向前移动
  • p9= sel和 传入的参数_cmd进行比较。如果相等走2流程
  • 如果不相等在判断(sel != 0 && bucket > 第一次确定的hash下标bucket)接着循环缓存查找,如果整个流程循环完仍然没有查询到或者遇到空的bucket。说明该缓存中没有缓存)sel = _cmd的方法,缓存查询结束跳转__objc_msgSend_uncached流程
  • mask向前遍历和前面的循环遍历逻辑基本一样

总结 objc_msgSend(recevier,_cmd) sel->imp

1.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