第11条:理解objc_msgSend的作用
1. 动态语言特性
在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全于运行期决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为一门真正的动态语言。
2. 消息派发(message dispatch)
*** 消息派发的一般过程 ***
给对象发送消息,如下:
id returnValue = [someObject messageName:parameter];
解释:someObject是“接收者”,messageName是“选择器”,选择器与参数合起来成为“消息”。
编译器在看到此消息后,将其转换为一条标准的C语言函数调用objc_msgSend,其原型如下:
void objc_msgSend(id self, SEL cmd, ...)
解释:这是个参数可变的函数。第一个参数代表接收者,第二个参数代表选择器,后续参数就是消息中的那些参数,其顺序不变。
对先前例子中的消息进行转换,如下:
id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);
解释:objc_msgSend函数会依据接收者与选择器的类型来调用适当的方法。为了完成此操作,该方法需要在接收者所属的类中搜寻其“方法列表”,如果能找到与选择器名称相符的方法,就跳至其实现代码。若是找不到,那就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。如果最终还是找不到相符的方法,那就执行“消息转发”(message forwarding)操作。
3. 消息派发的特殊情况
- objc_msgSend_stret:如果待发送的消息要返回结构体,那么可交由此函数处理。
- objc_msgSend_fpret:如果消息返回的是浮点数,那么交由此函数处理。
- objc_msgSendSuper:如果要给超类发消息,例如[super message:parameter],那么就交由此函数处理。
4. 尾调用优化
objc_msgSend等函数一旦找到应该调用的方法实现之后,就会“跳转过去”。之所以能这么做,是因为Objective-C对象的每个方法都可以视为简单的C函数,其原型如下;
<return_type> Class_selector(id self, SEL _cmd, ...)
解释:每个类里都有一张表格,其中的指针都会指向这种函数,而选择器的名称则是查表时所用的“键”。objc_msgSend等函数正是通过这张表格来寻找应该执行的方法并跳至其实现的。其中,所使用的“尾调用优化”(tail-call optimization)技术,可以让“跳至方法实现”这一操作变得更简单些。但是,只有当某函数的最后一个操作仅仅是调用其他函数而不会将其返回值另作他用时,才能执行“尾调用优化”。
要点
- 消息由接收者、选择器及参数构成。给某对象“发送消息”(invoke a message)也就相当于在该对象上“调用方法”(call a method)。
- 发给某对象的全部信息都要由“动态消息派发系统”(dynamic message dispatch system)来处理,该系统会查出对应的方法,并执行其代码。
网友评论