前言
上一篇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 | 条件比较 |
总结
- 开始走到汇编时,内心还是比较惶恐的,毕竟有很多不认识的符号。但静下心来细细分析,慢慢的简化了很多步骤,跟着符号一步一步的走,最终也走完了这个过程。