前言
我们知道,函数缓存到cache_t 提高了方法调用的访问速度。那方法调用的过程是怎么样呢
方法调用入口
我先先看下方法的调用接口, 我们在main函数中写个普通的调用接口
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
[person sayNB];
}
return 0;
}
然后在该目录下,使用clang命令编译下:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
编译完成后生成代码:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
}
return 0;
}
分析
在生成的cpp文件中,发现方法的调用基本最终是调用接口objc_msgSend,而sel_registerName是通过字符串生成SEL的接口。
SEL,方法的目录。
IMP, 方法的实现地址
objc_msgSend 的实现流程
通过代码查找,我们发现objc_msgSend底层使用汇编代码实现的,为什么用汇编呢
-
提高代码访问速度。
-
一些未知参数的识别,用C或者C++来实现比较困难

我们在进入CacheLookup查看方法获取流程

CheckMiss说明类的缓存中没有响应的IMP,会调用**__objc_msgSend_uncached**进行下一步查找,我们在进入源码看下
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
发现这里主要调用了MethodTableLookup,方法表查询,在进入源码,看看实现
.macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #-16]!
mov fp, sp
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// receiver and selector already in x0 and x1
mov x2, x16
bl __class_lookupMethodAndLoadCache3
// IMP in x0
mov x17, x0
看出来,这部分主要进行了参数的处理,以及调用了class_lookupMethodAndLoadCache3方法的调用,而看代码发现从这里开始就是c/c++函数了,就是慢速查找流程了。
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
总结
- ios 方法调用都是通过objc_msgSend进行的
- objc_msgSend首先会走快速查找流程
- 通过找到类的cache_t,通过哈希算法拿到key,再通过哈希表找到对象的imp
- 如果没有找到,就调用慢速查找流程