Effective Objective-C 2.0 第11条:理解objc_msgSend的作用

动态绑定

Objective-C是C的超集,C语言使用“静态绑定”,也就是说在编译期间就能决定运行时所应调用的函数。

#import <stdio.h>
void printHello() {
}
void printGoodbye() {
}
void doTheThing(int age) {
    if (type == 0)
        pirntHello();
    else
        printGoodbye();
    return;
}

如果不考虑“内联”,那么编译器在编译代码的时候就已经知道程序中有printHello与printGoodbye这两个函数了,函数地址实际是硬编码在指令之中的。

若doTheThing写成下列方式

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

这时就是使用“动态绑定”了,因为所有调用的函数直到运行期才能确定。

OC动态性

在OC中,如果对某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息以后,究竟该调用哪个方法则完全于运行期决定,给对象发送消息可以这么写:

id returnValue = [someOjbect messageName:parameter];
[接受者(receiver) 选择子(selector):参数];

编译器收到此消息后,转换成为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,其原型如下:

void objc_msgSend(id self, SEL cmd, ……)
【参数可变】

第一个参数代表接受者,第二个参数代表选择子(SEL是选择子类型),后续参数就是消息中的那些参数。编译器会把刚才的消息转换成:

id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);

先检查接收者的“方法列表”,如果能找到与选择子名称相符的方法,就跳至其实现代码,若是找不到就沿着其集成体系继续向上查找,等找到合适的方法之后跳转,如果最终还是找不到合适的方法,那就执行“消息转发”。

objc_msgSend会将匹配结果缓存在“快速隐射表”里面,每个类都有这么一块缓存,若是稍后稍后还向该类发送与选择子相同的消息,那么执行就快起来了。

objc_msgSend等函数一旦找到应该调用的方法实现之后,就会“跳转过去”,之所以这么做,是因为OC对象的每个方法都可以视为简单的C函数,原型如下:

<return_type> Class_selector(id self, SEL _cmd, ……)

真正的类名和函数名和上面可能不太一样,用“类”(Class)和“选择子”(selector)来命名是解释工作原理,每个类都有一个表格,其中的指针都会指向这种函数,而选择子的名称是查表时所使用的“键”。objc_msgSend正是通过这张表格来寻找应该执行的方法并跳转至其实现的,这里利用“尾调用优化”技术,令“跳转方法实现”这一操作变得简单。

如果一个函数的最后一项操作室调用另外一个函数,那么就可以运用“尾调用优化”技术。编译器会生成跳转至另一个函数所需的指令码,而且不会向调用堆栈中推入新的“栈帧”。只有当函数最后一个操作仅仅是调用其他函数而不会将其返回值另作他用时,才能执行“尾调用优化”技术。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值