iOS知识小集之为什么objc_msgSend()是用汇编实现的

消息发送


在使用Objective-C调用方法时,我们将其称之为消息发送,这与我们用的CC++等调用函数的说法不一样,原因就是Objective-C调用方法时,并不是简单的会在编译时得到函数指针,调用时直接使用该函数指针调用就行(C++有虚函数,包含一个v-table,可以实现简单的多态),而是会在调用的时候,运行时的去查找函数实现,比如,当我们发送[objc foo]时,编译器会将其转化为objc_msgSend(objc, @selector(foo))(注意,不一定都是转化为objc_msgSend,根据发送对象和返回类型,可转化为objc_msgSendSuper,objc_msgSendSuper_stret等),objc_msgSend方法负责查找函数实现并调用返回结果,我们知道,Objc Runtime开源的,所以我们可以看一下源代码objc_msgSend的实现逻辑。

objc_msgSend()使用汇编实现


源码可参见objc_msgSend源码,我们发现,竟然不是用C实现的,而是使用的汇编语言,总结来说,原因有二:

  1. 我们无法定义一个C函数,可以有可变的参数(可变参数是可以实现的,参考printf函数)并且可以调用任意的C函数指针,因为函数指针类型是在是无穷无尽的,根本就无法预先全部定义出来。
  2. 使用汇编另一个很重要的原因就是速度,首先,汇编就比C快,其次,通过使用汇编,可以免去大量局部变量拷贝的操作,参数会直接被存放在寄存器中,当找到IMP时,参数已经保存在了寄存器中,可以直接使用。

objc_msgSend()步骤总结


objc_msgSend步骤:

  1. 获取传递进来的类对象
  2. 获取类用来缓存方法的cache
  3. 使用selector在cache中查找
  4. 如果cache中查找不到,则跳转到C代码(_class_lookupMethodAndLoadCache3),进行slow search
  5. 调用方法的IMP

参考


  1. https://www.mikeash.com/pyblog/friday-qa-2017-06-30-dissecting-objc_msgsend-on-arm64.html
  2. https://github.com/zhongwuzw/objc4-cn/blob/087a6fd60e3cad2934163b26aa484640d6ff9467/runtime/Messengers.subproj/objc-msg-arm64.s
  3. https://github.com/zhongwuzw/objc4-cn