1.objc_msgSend快速查找
之前我们进入源码大致看了下objc_msgSend
汇编源码如下:
#endif
ENTRY _objc_msgSend //方法传入会有接受者和方法名
UNWIND _objc_msgSend, NoFrame
cmp p0, #0// 将接受者赋值给p0寄存器,同时判断接受者是否为0
//如果是0进行下面的判断
#if SUPPORT_TAGGED_POINTERS//是否支持target_pointers
b.le LNilOrTagged// 走下面的LNilOrTagged:方法
#else
b.eq LReturnZero //走下面的LReturnZero:方法,发送一个空的方法
#endif
ldr p13, [x0] // 把接受者的首地址也就是isa赋值给p13, p13 = isa
GetClassFromIsa_p16 p13, 1, x0 //通过这个方法拿到class 并赋值给 p16 = class,需要传入p13即 接收者的isa,1,以及接受者。
LGetIsaDone:
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached//获取到isa的话,进行缓存查找,
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check 检查对象是否为0,为0的话直接返回发送一个空方法
GetTaggedClass //获取小端对象的类
b LGetIsaDone//获取isa
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend//结束
复制代码
- 首先判断
recevier
是否为空,为空的话,判断是否支持小端,不支持的话直接返回空。 - 小端下,判断是否为负数,负数的话走
LNilOrTagged
方法。 - 进入
LNilOrTagged
后,判断小端,是否为空,为空的话返回空,不为空的话进入处理小端对象,获取isa
recevier
不为空的话,拿到isa,并通过GetClassFromIsa_p16
获取类的信息。在arm64架构下,通过isa
& isa_mask 进行内存偏移 获取到shiftClass
,拿到类的信息,并赋给P16;
1.1 GetClassFromIsa_p16
.macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if !needs_auth */
#if SUPPORT_INDEXED_ISA
// Indexed isa
mov p16, \src// optimistically set dst = src,把isa的值存放在寄存器P16
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // 判断是否是纯的isa。done if not non-pointer isa
// isa in p16 is indexed 索引
adrp x10, _objc_indexed_classes@PAGE// 把_objc_indexed_classes索引存入x10 寄存器
add x10, x10, _objc_indexed_classes@PAGEOFF // x10 = x10+ _objc_indexed_classes@PAGE 获取偏移 根据x10 进行内存偏移
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS //从p16的ISA_INDEX_SHIFT 到ISA_INDEX_BITS 位获取shiftclass 值 extract index
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__ //64位系统
.if \needs_auth == 0 // _cache_getImp takes an authed class already
mov p16, \src //是否已经获取类类的isa,获取类直接赋值
.else
// 64-bit packed isa
ExtractISA p16, \src, \auth_address//p16 = isa &isa_mask (获取shiftClass)
.endif
#else
// 32-bit raw isa
mov p16, \src 直接赋值p16 = isa
#endif
复制代码
- 主要通过isa进行内存偏移获取到
shiftClass
信息,并吧shiftClass 值赋值给p16.至此类的isa
查找完毕。
1.2CacheLookup查找
.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
mov x15, x16 // stash the original isa
LLookupStart\Function:
// p1 = SEL, p16 = isa
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS //判断是否是大端模式 模拟器
ldr p10, [x16, #CACHE]// p10 = mask|buckets
lsr p11, p10, #48 // p11 = mask
and p10, p10, #0xffffffffffff // p10 = buckets
and w12, w1, w11 // x12 = _cmd & mask
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 //判断是否是小端模式,真机为小端模式
ldr p11, [x16, #CACHE]// p11 = mask|buckets 把isa 首地址偏移16个字节,之前我们知道类的内存地址isa- superclass- cache 这样排的,获取cache值要首地址即isa平移16个字节就可得到cache的地址。
#if CONFIG_USE_PREOPT_CACHES //arm64真机
#if __has_feature(ptrauth_calls) //A12芯片
tbnz p11, #0, LLookupPreopt\Function //p11测试位判断是否为0,为0条转 LLookupPreopt\Function
and p10, p11, #0x0000ffffffffffff // p11即cache_t &0x0000ffffffffffff 拿到buckets 赋值给p10, p10 = buckets
#else //A12以下
and p10, p11, #0x0000fffffffffffe // p10 = buckets
tbnz p11, #0, LLookupPreopt\Function
#endif
eor p12, p1, p1, LSR #7 //异或(判断2个值是否相同,相同为0不同为1),p12 = p1^(p1>>7) = _cmd^(_cmd>>7)
and p12, p12, p11, LSR #48//p11>>48 相当于 _bucketsAndMask >> 48 得到mask p12 = (_cmd ^ (_cmd >> 7)) & mask 得到hash下的index, x12 = (_cmd ^ (_cmd >> 7)) & mask
#else 非arm64真机
and p10, p11, #0x0000ffffffffffff // p10 = buckets
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
#endif // CONFIG_USE_PREOPT_CACHES
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
ldr p11, [x16, #CACHE] // p11 = mask|buckets
and p10, p11, #~0xf // p10 = buckets
and p11, p11, #0xf // p11 = maskShift
mov p12, #0xffff
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
and p12, p1, p11 // x12 = _cmd & mask
#else //不支持就报错了
#error Unsupported cache mask storage for ARM64.
#endif
复制代码
- 首先通过类的isa 进行偏移16个单位拿到
cache_t
,赋值给p11
. - 通过高位抹零得到
buckets
,赋值给p10
. - 对
cache_t
进行偏移得到_bucketsAndMaybeMask
的地址,通过平移得到哈希算法下的index
// p10 = buckets首地址 ,p12 = index ,PTRSHIFT是3 一个buceet = sel(8字节)+imp(8字节)= 16字节, 通过首地址偏移获取bucket,存入p13
add p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// do {
1: ldp p17, p9, [x13], #-BUCKET_SIZE // 拿到bucket取出imp和sel{imp, sel} = *bucket--
cmp p9, p1 //比较是否不一样 if (sel != _cmd) {
b.ne 3f // 不一样继续查找 scan more
// } else {
2: CacheHit \Mode // 一样的找到imp hit: call or return imp
// }
3: cbz p9, \MissLabelDynamic // 如果一直找不到说明没有缓存,跳出去 if (sel == 0) goto Miss;
cmp p13, p10 // } 小于buckets数量 while (bucket >= buckets)
b.hs 1b
复制代码
- 执行
doWhile
循环,从_cmd & mask
得到index
开始取bucket
。判断是否有相等的sel=cmd
有的话就找到返回imp
。 - 如果一直找不到,跳出,进行慢速查找
__objc_msgSend_uncached
- 如果第一个
bucket
是buckets
的第一个元素则,把bucket
设置为最后一个元素,进行后面的二次循环(bucket > first_probed
) - 如果
bucket
不是buckets第一个则继续向前查找
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16_BIG_ADDRS
add p13, p10, w11, UXTW #(1+PTRSHIFT) //在非真机下
// p13 = buckets + (mask << 1+PTRSHIFT)
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
add p13, p10, p11, LSR #(48 - (1+PTRSHIFT))//真机下
// p13 = buckets + (mask << 1+PTRSHIFT)
// see comment about maskZeroBits
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
add p13, p10, p11, LSL #(1+PTRSHIFT)
// p13 = buckets + (mask << 1+PTRSHIFT)
#else//错误
#error Unsupported cache mask storage for ARM64.
#endif
add p12, p10, p12, LSL #(1+PTRSHIFT)//p10 = buckets,p12
// p12 = first probed bucket
// do {
4: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
cmp p9, p1 // if (sel == _cmd)
b.eq 2b // goto hit
cmp p9, #0 // } while (sel != 0 &&
ccmp p13, p12, #0, ne // bucket > first_probed)
b.hi 4b //如果当前的bukect和p12(根据_cmd和mask 算出index 取出的bucket)相等直接接miss
LLookupEnd\Function:
LLookupRecover\Function:
b \MissLabelDynamic
复制代码
二次循环
,和上面的dowhile循环一样,值得注意的是如果当前的第一个bukect和p12(根据_cmd和mask 算出index 取出的bucket)相等直接返回MissLabelDynamic
2.总结
- 获取当前消息的接受者,进行判断
是否存在
,不支持小端模式直接返回空,支持的话进行小端模式获取isa
- 获取isa后进行内存平移16个单位获取
*cache_t
,拿到buckets
,根据_cmd
和mask
获取哈希算法下的index
。 - 根据
index
取buckets
里的bucket
,bucket
中的sel
和_cmd
比较,相等的话就找到了,不相等就进行递归*buckets--
查找,直到bucket 等于buckets第一个元素。
- 没找到的话进行
二次递归
,定位到buckets
的最后一个
元素,取出bucket
,往前进行递归查找。如果当前的bukect和p12(根据_cmd和mask 算出index 取出的bucket)
相等还没找到直接返回MissLabelDynamic
走慢速查找
下面是一张大概的流程图
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END