编写高质量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用这张表来寻找应该执行的方法并跳转实现。
“尾调用优化”:如果函数的最后一项操作是调用另外一个函数,编译器会生成调转至另一个函数所用的指令码。不会向调用堆栈中推入新的“栈帧”。
注意:只有当函数的最后一项操作仅仅是调用其他函数而不会将其返回值另做他用时
#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用这张表来寻找应该执行的方法并跳转实现。
“尾调用优化”:如果函数的最后一项操作是调用另外一个函数,编译器会生成调转至另一个函数所用的指令码。不会向调用堆栈中推入新的“栈帧”。
注意:只有当函数的最后一项操作仅仅是调用其他函数而不会将其返回值另做他用时
相关文章推荐
- 编写高质量OC代码52建议总结:22.理解NSCopying协议
- 编写高质量OC代码52建议总结:21.理解Objective-C的错误模型
- 编写高质量OC代码52建议总结:23.通过委托与数据源协议进行对象间通信
- 编写高质量OC代码52建议总结:25.总是为第三方类的分类名称加前缀
- 编写高质量OC代码52建议总结:20.为私有方法加前缀
- 编写高质量OC代码52建议总结:13.用“方法调配技术”调试“黑盒方法”
- 编写高质量OC代码52建议总结:19.使用清晰而协调的命名方式
- 编写高质量OC代码52建议总结:26.不要在分类中设置属性
- 写高质量OC代码52建议总结:44.通过Dispatch Group机制,根据系统资源状况来执行任务
- 编写高质量OC代码52建议总结:17.实现describtion方法
- 编写高质量OC代码52建议总结:18.尽量使用不可变对象
- 编写高质量OC代码52建议总结:24.将类的实现代码分散到便于管理的数个分类中
- 写高质量OC代码52建议总结:32.编写“异常安全代码”时留意内存管理问题
- 编写高质量OC代码52建议总结:10.关联对象
- 编写高质量OC代码52建议总结:16.提供“全能初始化方法”
- 编写高质量OC代码52建议总结:27.使用“class-continuation 分类” 隐藏实现细节
- 写高质量OC代码52建议总结:29.理解引用计数
- 编写高质量OC代码52建议总结:9.以“族类模式“隐藏实现细节
- Effective Objective-C 2.0 编写高质量iOS与OS X代码 objc_msgSend 的作用
- 写高质量OC代码52建议总结:42.多用GCD,少用performSelector系列方法