前言
上一篇Runtime方法快速查找(上)中,我们读取缓存时先拿到了class
,本文将继续进行查找。
CacheLookup
我们拿到class
后,LGetIsaDone
是代码往下走的意思
继续走的代码中提到个新函数CacheLookup
,并传入三个参数NORMAL
, _objc_msgSend
, __objc_msgSend_uncached
,我们进入方法再进行分析:
获取bucket
和查找index
- 这里主要有几个步骤:
-
- 获取
cache_t
:ldr p11, [x16, #CACHE]
,这里x16
是p16
地址,我们知道p16
是class
,而#CACHE
是16字节
,所以此处是:class
地址平移16个字节
,刚好取到cache_t
。
- 获取
-
- 获取
buckets
:#0x0000fffffffffffe
是maskShift
,p11 & maskShift
得到buckets
,赋值给p10
。
- 获取
-
- 获取第一次查找的
index
:p1
是_cmd
,LSR
是>>
,eor
是^(异或)
。首先p12 = p1 ^ (p1 >> 7)
,再p12 = p12 & mask
就得到了要查找的下标index
。
- 获取第一次查找的
-
遍历查找
bucket >= buckets
- 先获取到要查找的
bucket
,这里获取bucket
的方式是内存平移
,相当于b[i]
一样,然后我们进行遍历查找。 ldp
是同时操作两个寄存器,首先将buckets地址左移一个bucket单位
,然后得到的imp
和sel
分别赋值给p17
和p9
。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
。 - 查找过程如下:
- 如何这个判断里没有找到,将继续在下面判断查找
bucket > first_probed
- 这里分为三步:
-
- 获取最后一个
bucket
- 获取最后一个
-
- 获取
index
处的bucket
- 获取
-
- 从最后一个
bucket
往index
处的bucket
遍历。 查找图解如下:
- 从最后一个
-
- 如果执行完,还是没有找到,就会走
__objc_msgSend_uncached
方法:
缓存查找流程
- 整个的缓存查找流程如下:
汇编相关符号
汇编符号 | |
---|---|
cmp | 比较 |
b.le | 小于 |
b.eq | 相等 |
ldr | 将指令读到目标寄存器 |
mov | 将数据存到寄存器 |
and | &(按位与) |
tbnz | 不为0跳转 |
LSR | >>(右移) |
eor | ^(按位异或) |
LSL | <<(左移) |
ldp | 操作两个寄存器 |
b.ne | 不相等 |
cbz | 是否存在,不存在跳转 |
br | 跳转指令 |
b.hs | 大于等于 |
b.hi | 大于 |
ccmp | 条件比较 |
总结
- 开始走到汇编时,内心还是比较惶恐的,毕竟有很多不认识的符号。但静下心来细细分析,慢慢的简化了很多步骤,跟着符号一步一步的走,最终也走完了这个过程。