- 先从源码objc-msg-arm64.s看起
- p0是消息的接受者,判断是否存在,存在将p0平移到p13
上面的代码提取关键部分,可以得到
- tbnz p11, #0, LLookupPreopt\Function // 判断 p11 的 0 号位置是否为0? 不为0的话走
- LLookupPreopt\Function,LLookupPreopt查找共享缓存
- andp10, p11, #0x0000ffffffffffff // p10 = buckets
- LLookupStart -> CACHE_MASK_STORAGE_HIGH_16 //高16位真机架构
- ldr p11, [x16, #CACHE] // 获取cache // p11 = mask|buckets
- eor p12, p1, p1, LSR#7
这里为什么要右移7位呢?
- 看下面的insert函数
- 因为你在原始的时候,是一个读的过程,就得插入,如果当前要插的数据和sel一样的话,意味着插入的时候index右移了7,与上mask 得到值,再存起来,等取的时候,通过index取出来,所以两者的hash函数是一致的。就得出了 value ^= value >>7。
and p12, p12, p11, LSR#48 这里为什么要右移48位呢?
- 因为总共64位,前48位是bucket,后16是mask,右移出去,前面用0填充,得到mask。得到hash的inex。
- add p13, p10, p11, LSR#(48 - (1+PTRSHIFT))
- // p13 = buckets + (mask << 1+PTRSHIFT)
- // see comment about maskZeroBits
mask等于7,7左移4位,就是7*16,已经到最后的位置,即p13就是最后的位置
- CacheHit \Mode 缓存命中
- MissLabelDynamic 没命中走
- __objc_msgSend_uncached
- isKnownClass 当前的class是否已经注册到当前的缓存表里面了
缓存为什么要用混编写?而不是c++?
- 1、缓存是为了提高效率,让当前的对象快速找到缓存。
- 2、缓存的参数可能是未知的,汇编会更加的动态化。
lookUpImpOrForward 慢速查找 不断的遍历methodlist
慢速查找流程:
- 1、查自己 methodlist-sel-imp (curClass method list.)
- 2、父类->NSObject->nil->跳出
小结:
- 1: cmp p0, #0 //判断p0是否存在
- 2: GetClassFromIsa_p16 p13,1, x0 // p16 = class
- 3: CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
3.1: LLookupStart -> CACHE_MASK_STORAGE_HIGH_16 //真机架构
ldr p11, [x16, #CACHE] // 获取cache // p11 = mask|buckets
CONFIG_USE_PREOPT_CACHES ==1
tbnz p11, #0, LLookupPreopt\Function // p11 的 0 号位置是否为0 不为0 -> LLookupPreopt
**and **p10, p11, #0x0000ffffffffffff // p10 = buckets
// p1 sel >> 7 == value ^= value >> 7;
eor p12, p1, p1, LSR #7
// 哈希 index
**and **p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask
// index << 4
// 2 * 16
// buckets + 32
// 对应下标 : p13 第一个要查bucket(sel imp)
add p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// sel -> imp 。缓存里的bucket sel 是否匹配,匹配的话返回imp
未完待续......
网友评论