在对象上调用方法,就叫做“传递消息”。消息有“名称”或“选择器”(方法的名字),可以接受参数,可能有返回值。
C语言的函数调用,使用“静态绑定”,在编译期就能决定所应调用的函数。
在OC中,如果向某个对象传递消息,也就是调用对象的方法,会使用“动态绑定”机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息后,调用那个方法完全由运行期决定,甚至可以在运行时改变。
给对象发送消息,一般这样写:
id returnValue = [someObject messageName:parameter];
说明:
someObject:接受者
messageName:选择器
选择器与参数合起来成为“消息”
编译器看到这条消息后,将其转化为一条标准的C语言函数调用
// 消息传递机制的核心函数
void objc_msgSend(id self, SEL cmd, ...)
说明:
是一个“参数可变的函数”,能接受两个或两个以上的参数。
第一个参数:接受者
第二个参数:选择器(SEL是选择器的类型)
后续参数就是消息中的参数,顺序不变。
选择器指的就是方法的名字。
编译器将例子中的消息转换为:
id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);
消息调用的过程:
objc_msgSend函数会依据接受者(类、对象)与选择器的类型(对象方法,类方法)调用适当的方法。
上面的方法需要在接受者(对象)所属的类中搜寻其“方法列表”,如果能找到与选择器名称相符的方法,就跳至其实现代码。若是找不到,那就沿着继承体系继续向上查找,等找到合适的方法在跳转。如果最终找不到相符的方法,那就执行“消息转发”。
在这个过程中,objc_msgSend会将匹配结果缓存在“快速映射表”里面,每个类都有这样一块缓存,若是稍后还向该类发送与选择器相同的消息,那执行起来就快了。
其他“边界”情况,则需要交由OC运行环境的其他函数处理:
objc_msgSend_stret:待发送消息要返回结构体,且返回值大小CPU的结存器能够容纳,用此函数处理消息,若无法容纳,由另一个函数执行,通过分配在栈上的某个变量来处理消息所返回的结构体。
objc_msgSend_fpret:消息返回浮点数。
objc_msgSendSuper:要给超类发送消息。还有与上面两个函数等效的用于处理发给super消息的函数。
objc_msgSend等函数一旦找到应该调用的方法实现之后,就会“跳转过去”,是因为OC对象的每个方法都可以视为简单的C函数,其原型如下:
<return_type> Class_selector(id self, SEL _cmd, ...)
每个类里都有一张表格,其中的指针都会指向这种函数,而选择器的名称则是查表时所用的“键”,objc_msgSend等函数正是通过这张表格来寻找应该执行的方法并跳至其实现的。
网友评论