一、概述
objc_msgSend这个方法对于iOS开发者来说是再熟悉不过了,无论是初学还是中级最后进阶资深都离不开这个方法。在iOS中调用方法实质上就是给某个对象发送消息。消息的发送在编译的时候编译器就会把方法转化为objc_msgSend这个函数。objc_msgSend有两个隐式的参数,消息的接收者和消息的方法名。objc_msgSend这个函数就能通过这两个隐形的参数找到对应的方法具体实现。
- 如果消息的接收者是实例对象,isa就指向该类对象,后再通过第二个参数方法名,去寻找对应的实现。
- 如果消息的接收者是类对象,isa就指向他的元类,就会去元类里面寻找对应的方法实现。
二、objc_msgSend方法的实现
我们从源码进行探索,源码版本号是objc4-838.1,全局搜索objc_msgSend,可以看到有很多,在arm64架构下的,有i386下的,有模拟器x86下面的,我们现在看一下在arm64架构下面的。
我们可以看到在真机arm64构架下,方法是由汇编写的,至于为什么会用汇编,那肯定是为了提高效率嘛。
三、对应的真机调试
1.首先进入objc_msgSend流程,
cmp p0判断p0是否存在,我们从读取寄存器里面可以看到,X0寄存器是存了一个LGPerson的实例对象,X1是study方法。
2.通过x0 x/4gx打印出isa指针,isa指针地址是
0x000021a104045789,恰好x13寄存器的地址就是0x000021a104045789,说明x13寄的是isa地址。通过isa指针找到对应的类,打印x16的地址,x16 = 0x0000000104045788 (void *)0x0000000104045760: LGPerson,存的就是类对象地址。
3.找到类对象了,下一步该通过类对象找方法的实现。
4.开始找方法,取出x16的class移到x15,通过x16找cache
5.
ldr x11, [x16, #0x10]通过内存平移可以得到x11,x11就是之前cache内的第一个_backetsAndMaybeMask
6.and x10, x11, #0xfffffffffffe得到buckets的首地址x10。然后在cache进行方法查找。如果找到会CacheHit \Mode,找不到的话会调用_objc_msgSend_uncached,一直循环直到找到对应的方法。
消息的快速查找流程:
- 判断receiver(消息的接收者)是否存在。
- receiver->isa->class
- class内存平移->cache
- cache->buckets
- 遍历buckets -> bucket(sel,imp)对⽐sel
- 如果bucket(sel,imp)对⽐sel 相等 --> cacheHit --> 调⽤imp
- 如果cache⾥⾯没有找到对应的sel --> _objc_msgSend_uncached
四、方法的快速查找
1.方法在Cache内找不到,那就会去调用_objc_msgSend_uncached函数
2.在
_objc_msgSend_uncached函数内部可以看到调用MethodTableLookup,内部又会跳转_lookUpImpOrForward
消息的慢速查找
lookUpImpOrForward -> 先找当前类的methodList -> superClass的cache -> superClass的 methodList -> 直到superClass为nil。 若父类为空的时候,还未找到,则会进入消息转发。