一、前言
我们都知道iOS的方法调用其实就是消息发送,到最后都会调用objc_msgSend
方法。这个方法的调用实在是太频繁,为了提高调用的性能,苹果将这个方法的实现用汇编语言来写。汇编更接近于底层的机器语言,执行性能比高级语言更好。
但正是这个缘故,很多人对于objc_msgSend
的探索也止步于此:
望门兴叹😤😤😤
下面,我们从别人脚步停止的地方开始,从汇编源码的角度,窥探objc_msgSend
到底做了什么。
二、objc_msgSend主要流程
我们以objc-msg-arm64.s
为例,不同的架构流程有所不同。
1、通过对象的isa
指针找到对应的类。
2、从类的cache_t
中找到缓存数组buckets
。
3、计算哈希下标,这个下标用来访问buckets
数组元素。之前插入方法缓存到buckets
中时需要计算哈希下标,现在访问时也需要。
4、比较目标sel
和bucket
中的sel
,判断是否为我们需要的bucket
。
5、找到目标bucket
后,提取imp
进行调用。
💡 如何快速读懂整个流程
这里整理了整个流程下来会经常使用的寄存器,以及寄存器里存放的数据。这些寄存器里存放的数据基本不会改变:
X0:receiver,objc_msgSend函数的调用者
X1:sel
X10:buckets数组
X11:mask | buckets
X12:哈希下标
X13:遍历到的bucket所在位置
X16:isa/class
X17:imp
如果你读源码到某个位置懵了,可以回来这里整理一下思路😑
1、GetClassFromIsa_p16
根据isa
,获取对应的class
,并放到p16寄存器里。
函数入参
src
:p13(isa)
needs_auth
:1
auth_address
:x0
① ExtractISA
这一步是真正去获取class的操作。isa & ISA_MASK
之后,将结果(class)存在p16寄存器。
ExtractISA 入参
$0
:p16
$1
:isa
2、CacheLookup
根据sel
,从对应的类中去寻找缓存bucket
。
函数入参
Mode
:NORMAL
Function
:_objc_msgSend
MissLabelDynamic
:__objc_msgSend_uncached
MissLabelConstant
:nil
这个方法相对来说比较长,且都是汇编。汇编确实晦涩难懂,但好在苹果还是比较贴心,很多地方都给出了注释。只要你懂得缓存是怎么写入的,那你一定能看懂下面的代码,缓存的读取其实就是反过来的过程。
我们将它分割成四个部分:
① 计算buckets数组的位置和哈希下标
我们首先要找到buckets
数组的位置,以及根据sel
算出缓存所在的下标,才能从buckets
数组中找到对应的缓存。
② 在左半部分寻找缓存bucket
从哈希下标内存位置向前遍历,遍历到buckets
0号内存位置为止。
这时候我们已经找到了哈希下标,但是当初插入方法缓存时,我们有处理哈希冲突的情况,实际插入的位置可能有偏移。现在确定的哈希下标位置可能不是当初真正的插入点,所以我们需要将sel
和找到的bucket
中的sel
做比较,如果不一致,就继续往左边的位置遍历寻找,直到遍历到buckets
数组的0号位置。
在这个过程中,如果sel
等于buckct
中的sel
,则调用CacheHit
函数。如果直到bucket中
的sel
等于0时,依然没有找到,则说明已经到了缓存的末尾了,因为插入的时候是向左遍历插入的。之后会调用MissLabelDynamic
对应的方法。
③ 获得buckets数组的最后位置
如果从哈希下标开始找,直到buckets
数组的0号位置(左半部分)都找不到对应的bucket
,我们就需要找一下buckets
的右半部分了。我们先找到buckets
数组的最后位置(内存位置,非下标),再往前遍历。
④ 在右半部分寻找缓存bucket
从buckets
最后的内存位置,向前遍历,直到遍历到哈希下标所在的内存位置为止。
3、CacheHit
缓存命中,找到sel
对应的缓存bucket
时会调用。在这个方法里,一般来说会直接调用找到的imp
。
缓存查找分为3个模式:
1、normal
:最普遍的情况。我们调用某个方法时,先查找有没有对应的方法缓存,有的话就调用,没有的话再去类方法列表继续查找。
2、getImp
:单纯的获取方法缓存,不会执行方法。
3、lookup
:这个暂时不清楚使用场景。按字面意思,也许只是找一下有没有这个方法缓存?
三、总结
本节梳理了objc_msgSend
底层实现的主要流程。其他的一些异常处理流程,包括没找到方法缓存时应该如何处理等,就属于慢速查找流程、动态方法协议之类的知识了。这个后续再慢慢梳理。
不得不说,梳理底层基础知识真的是个漫长的过程。😂
路漫漫其修远兮,吾将上下而求索。