iOS底层-Runtime方法快速查找(下)

433 阅读2分钟

前言

上一篇Runtime方法快速查找(上)中,我们读取缓存时先拿到了class,本文将继续进行查找。

CacheLookup

我们拿到class后,LGetIsaDone是代码往下走的意思

截屏2021-07-02 16.51.28.png
继续走的代码中提到个新函数CacheLookup,并传入三个参数NORMAL_objc_msgSend __objc_msgSend_uncached,我们进入方法再进行分析:

获取bucket和查找index

截屏2021-07-02 17.34.16.png

  • 这里主要有几个步骤:
      1. 获取cache_tldr p11, [x16, #CACHE],这里x16p16地址,我们知道p16class,而#CACHE16字节,所以此处是:class地址平移16个字节,刚好取到cache_t
      1. 获取buckets#0x0000fffffffffffemaskShiftp11 & maskShift得到buckets,赋值给p10
      1. 获取第一次查找的indexp1_cmdLSR>>eor^(异或)。首先p12 = p1 ^ (p1 >> 7),再p12 = p12 & mask就得到了要查找的下标index

遍历查找

bucket >= buckets

截屏2021-07-02 17.57.51.png

  • 先获取到要查找的bucket,这里获取bucket的方式是内存平移,相当于b[i]一样,然后我们进行遍历查找。
  • ldp是同时操作两个寄存器,首先将buckets地址左移一个bucket单位,然后得到的impsel分别赋值给p17p9
  • p1是要存的sel,当和p9不同时,走步骤3,当sel不存在时,走MissLabelDynamic,当第一个查找的bucket>buckets首地址时,buckets地址再左移一个bucket单位继续对比。
  • p1 = p9,则缓存命中,走CacheHit
// CacheHit: x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa
.macro CacheHit
.if $0 == NORMAL
	TailCallCachedImp x17, x10, x1, x16	// authenticate and call imp


//
.macro TailCallCachedImp
	// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
// CacheHit: x17 = cached IMP, x10 = address of buckets, x1 = SEL, x16 = isa
	eor	$0, $0, $3 // eor 是按位异或     x17 ^ class 编码imp
	br	$0
  • 这里是对imp进行解码,然后直接返回imp
  • 查找过程如下:

截屏2021-07-03 14.20.56.png

  • 如何这个判断里没有找到,将继续在下面判断查找

bucket > first_probed

截屏2021-07-03 14.41.03.png

  • 这里分为三步:
      1. 获取最后一个bucket
      1. 获取index处的bucket
      1. 从最后一个bucketindex处的bucket遍历。 查找图解如下:

截屏2021-07-03 14.55.54.png

  • 如果执行完,还是没有找到,就会走__objc_msgSend_uncached方法:

截屏2021-07-03 14.54.49.png

缓存查找流程

  • 整个的缓存查找流程如下:

截屏2021-07-03 19.47.37.png

汇编相关符号

汇编符号
cmp比较
b.le小于
b.eq相等
ldr将指令读到目标寄存器
mov将数据存到寄存器
and&(按位与)
tbnz不为0跳转
LSR>>(右移)
eor^(按位异或)
LSL<<(左移)
ldp操作两个寄存器
b.ne不相等
cbz是否存在,不存在跳转
br跳转指令
b.hs大于等于
b.hi大于
ccmp条件比较

总结

  • 开始走到汇编时,内心还是比较惶恐的,毕竟有很多不认识的符号。但静下心来细细分析,慢慢的简化了很多步骤,跟着符号一步一步的走,最终也走完了这个过程。