小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
快速查找流程:
- 将类对象地址,内存平移
16字节
,取地址,得到cache
首地址,即:_bucketsAndMaybeMask
_bucketsAndMaybeMask & bucketsMask
,获取buckets
首地址- 判断
_bucketsAndMaybeMask
的0
号位不为0
,进入LLookupPreopt
流程,查找共享缓存 - 否则,通过
_bucketsAndMaybeMask >> 48
,得到mask
(_cmd ^ (_cmd >> 7)) & mask
,得到下标i
。源码中的cache_hash
函数- 通过
i * 16
得到偏移值,buckets首地址+偏移值
,得到指定下标的bucket
流程1
:- 读取
bucket_t
中的imp
和sel
- 通过
bucket_t - 16字节
,读取上一个bucket
- 读取
流程2
:- 如果
sel
存在,并且等于_cmd
,进入CacheHit
缓存命中流程 -CacheHit
流程:使用imp = imp ^ cls
解码 - 跳转到指定
imp
函数地址
- 如果
流程3
:- 如果
sel
不存在,进入__objc_msgSend_uncached
流程 - 上一个
bucket
地址和buckets
首地址比较,如果>=
,进入流程1
- 否则,
<
首地址,获取mask
下标的bucket
,进入流程4
- 如果
流程4
:- 读取
bucket_t
中的imp
和sel
- 通过
bucket_t - 16字节
,读取上一个bucket
- 如果
sel
等于_cmd
,进入流程2
- 如果
sel
存在,并且上一个bucket
地址>
指定下标bucket
地址,进入流程4
- 否则,进入
__objc_msgSend_uncached
流程
- 读取
消息快速查找流程中,如果无法命中缓存,进入MissLabelDynamic
流程。而MissLabelDynamic
即是调用CacheLookup
时传入的__objc_msgSend_uncached
流程探索:
- 核心流程:
__objc_msgSend_uncached
→MethodTableLookup
→_lookUpImpOrForward
lookUpImpOrForward
函数,并且不是汇编代码实现,而是C/C++
函数- 汇编和C/C++的相互调用:
C/C++
中调用汇编,在汇编代码中查找时,在方法名称最前面加一个下划线- 汇编中调用
C/C++
函数,在C/C++
代码中查找时,去掉方法名称最前面的一个下划线