理解objc_msgSend的作用

在对象上调用方法是OC中经常使用的功能,用OC的术语来说,这叫做“传递消息”。消息有“名称”或者“选择子”,可以接受参数,而且可能还有返回值。
OC是C的超集,所以最好先理解C语言的函数调用方式。C语言使用“静态绑定”,顾名思义:在编译期就能决定运行期所应该用的函数。

#import 
void printHello () {
  printf("Hello, world!\n");
}
void printGoodbye () {
  printf("Goodbye, world!\n");
}
void doTheThing(int type) {
  if (type == 0) {
    printHello();
  } else {
    printGoodbye();
  }
  return 0;
}

如果不考虑“内联”,那么编译器在编译代码的时候就已知道程序中有printHello和printGoodbye这两个函数了,于是会直接生产调用这些函数的指令。而函数地址实际上是硬编码在指令之中的,若是将刚才那段代码写成下面这样,就不一样了。

#import 
void printHello () {
  printf("Hello, world!\n");
}
void printGoodbye () {
  printf("Goodbye, world!\n");
}
void doTheThing(int type) {
void (*test) ();
  if (type == 0) {
    test = printHello;
  } else {
    test = printGoodbye;
  }
  test ();
  return 0;
}

这时候就得使用“动态绑定”了,因为所要调用的函数直到运行期才能确定,编译器在这样的情况下生成的指令和刚才那个例子不同,在第一个例子中,if和else语句里都有函数的调用指令,而在第二个例子中,只有一个函数调用的指令,不过调用的函数地址无法硬编码在指令张,二手要在运行期读取出来。

在OC中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法,在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全在运行期决定,甚至可以在程序运行时改变,这些特性使得OC成为一门真正的动态语言。

给对象发送消息可以这样来写:

id returnValue = [someObject messageName:parameter];

在本例中,someObject叫做“接收者”,messageName叫做“选择子”。选择子与参数合起来称为“消息”。编译器看到此消息后,将其转换为一天标准的C语言函数调用,所调用的函数乃是消息传递机制中核心函数:objc_msgSend,其“原型”如下:

void objc_msgSend(id self, SEL cmd, ...)

这是个“参数个数可变的函数”,能接受两个或两个以上的参数,第一个参数代表接收者,第二个参数代表选择子(SEL是玄子的类型),后续参数就是消息中的那些参数,其顺序不变,选择子指的就是方法的名字。“选择子”与“方法”这两个词经常交替使用。编译器会把刚才 那个例子中的消息转换为如下函数:

id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);

objc_msgSend函数会依据接收者与选择子的类型来调用适当的方法,为了完成此操作,该方法需要在接收者所属的类中搜寻其”方法列表“,如果能找到与选择子名称相符合的方法,就跳到其实现代码,若是找不到,就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。如果最终还是找不到相符的方法,那就执行”消息转发“操作。

这么一说,向调用一个方法似乎需要很多步骤,所幸objc_msgSend会将匹配结果缓存在”快速映射表“里面,每个类都有这样一块缓存,若是稍后还向该类发送与选择子相同的消息,那么执行起来就很快了,当然了,“快速执行路径”还是不如“静态绑定的函数调用操作”那么迅速,不过只要把选择子缓存起来,那么就不会很慢。实际上,消息派发并非应用程序的瓶颈所在,假如真是个瓶颈的话,那可以只编写纯C函数,在调用时根据需要,把OC对象的状态传进去。

前面说的这部分内容只描述了部分消息的调用过程,其他“边界情况”则需要交给OC运行环境中的另外一些函数来处理:objc_msgSend_stret(返回结构体),objc_msgSend_fpret(返回浮点型),objc_msgSendSuper(要给超类发消息,[super message:parameter])。

总结:

  • 消息由接收者,选择子和参数构成,给某个对象“发送消息”也就相当于在该对象上“调用方法”。
  • 发给某个对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出相应的方法,并执行其代码。

你可能感兴趣的:(理解objc_msgSend的作用)