您的位置:首页 > 编程语言

编写高质量OC代码52建议总结:11.理解objc_msgSend的作用(消息机制)

2017-02-09 19:34 549 查看
在对象上调用方法,术语叫做“传递消息”,消息有“名称”和“选择器(方法)”,可以接收参数,还可能有返回值。OC是C的超集,C语言使用静态绑定,在编译期间就能决定运行时做调用的函数。

#include <stdio.h>

void printHello() {
printf("hello, world!\n");
}

void printGoodbye() {
printf("Goodbye, world!\n");
}

void doTheThing(int type) {
if (type == 0) {
printHello();
} else {
printGoodbye();
}
}

编译器在编译代码的时候就知道有printHello和printGoodbye两个函数了,会直接生成调用这些函数的指令,函数地址实际上是硬编码在指令之中。
#include <stdio.h>

void printHello() {
printf("hello, world!\n");
}

void printGoodbye() {
printf("Goodbye, world!\n");
}

void doTheThing(int type) {
void (*fnc)();
if (type == 0) {
fnc = printHello;
} else {
fnc = printGoodbye;
}
fnc();
}

如果代码变成这样,就得使用“动态绑定”,因为所要调用的函数直到运行期才能确定,第一个例子中,if 和 else 中都有函数调用指令,第二个例子中只有一个函数调用指令,待调用的函数地址无法硬编码在指令中,要在运行期读取出来。
给对象发送消息可以这么写:

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 会在接受者所属的类中搜寻其“方法列表”,如果找到与“选择器”名称相符的代码就实现,没找到就延继承体系向上查找,最终找不到,就实现“消息转发”。
备注:每个类都有一块缓存“快速映射表”,如果稍后还向该类发送相同的消息,执行就快。

运行环境中一些“边界情况”,需要其他函数处理

  1.objc_msgSend_stret:如果待发送的消息要返回结构体,交给这个函数。

  2.objc_msgSend_fpre:如果消息返回的是浮点数,交给这个函数。

  3.objc_msgSendSuper:给超类发消息,用这个函数。也有与objc_msgSend_fpre和objc_msgSend_fpre等效的处理超类消息的方法。

 

OC对象的每个方法都可以看做简单的C函数,原型如下:
<return_type> Class_selector(id self, SEL _cmd, ...)每个类里都会有一张表格,其中的指针都会指向这种函数,选择器的名称则是查表时用的“键”,objc_msgSend用这张表来寻找应该执行的方法并跳转实现。

  
“尾调用优化”:如果函数的最后一项操作是调用另外一个函数,编译器会生成调转至另一个函数所用的指令码。不会向调用堆栈中推入新的“栈帧”。

注意:只有当函数的最后一项操作仅仅是调用其他函数而不会将其返回值另做他用时
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐