返回
探寻objc_msgSend方法查找的秘密
IOS
2023-09-20 08:27:36
在之前的文章中,我们已经对方法存储类的cache_t进行了分析。我们还提到过这样一个问题:如果cache_t中已经存在该方法,再次调用该方法的时候,不会走cache_t的写入方法。今天我们就来探究一下原因。
查看方法调用最直观的方式就是用clang。下面是我用clang对objc_msgSend的调用过程进行反编译的结果:
objc_msgSend(id, SEL, ...) __attribute__((noreturn)) __attribute__((visibility("hidden")))
从反编译结果可以看出,objc_msgSend函数的参数包括接收者id、选择器SEL和可变数量的参数。objc_msgSend函数的实现非常复杂,涉及到多个子函数的调用。为了便于理解,我们将整个过程分解成几个步骤:
- 首先,objc_msgSend函数会根据接收者的类型查找对应的cache_t结构。
- 然后,objc_msgSend函数会在cache_t结构中查找与选择器相匹配的方法。
- 如果在cache_t结构中找到了与选择器相匹配的方法,则直接调用该方法。
- 如果在cache_t结构中没有找到与选择器相匹配的方法,则objc_msgSend函数会通过其他方式查找该方法。
在步骤3中,我们提到了“直接调用该方法”。这也就是说,如果cache_t结构中已经存在该方法,再次调用该方法的时候,不会走cache_t的写入方法。这是因为,cache_t结构本身就是一个缓存,它的作用是加速方法查找。如果cache_t结构中已经存在该方法,则直接调用该方法可以节省查找时间。
当然,cache_t结构的缓存也不是万能的。当cache_t结构中没有找到与选择器相匹配的方法时,objc_msgSend函数会通过其他方式查找该方法。这些其他方式包括:
- 在接收者的父类中查找该方法。
- 在接收者的协议中查找该方法。
- 在动态库中查找该方法。
通过这些方式,objc_msgSend函数最终都会找到与选择器相匹配的方法。但是,这些方式的效率要比直接从cache_t结构中查找方法低。因此,为了提高应用程序的性能,我们应该尽量避免在cache_t结构中找不到与选择器相匹配的方法的情况发生。
我们可以通过以下几种方式来避免这种情况:
- 确保在使用cache_t结构之前,已经将所有可能被调用的方法都添加到cache_t结构中。
- 尽量减少接收者的父类和协议的数量。
- 尽量避免使用动态库。
通过这些方式,我们可以提高cache_t结构的命中率,从而提高应用程序的性能。