第11条:理解objc_msgSend的作用

2018-08-12  本文已影响11人  MrSYLong

在对象上调用方法,就叫做“传递消息”。消息有“名称”或“选择器”(方法的名字),可以接受参数,可能有返回值。

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等函数正是通过这张表格来寻找应该执行的方法并跳至其实现的。

上一篇下一篇

猜你喜欢

热点阅读