在对象上调用方法是包括Objective-C的众多语言都具备的功能。但在Objective-C中,这个术语叫『传递消息』(pass a message)。『消息』有「名称」(name)或「选择子」(selector),可以接受参数,也可能有返回值。

静态绑定和动态绑定

由于Objective-C是C的超集,所以最好先理解C语言的函数调用方式。C语言使用「静态绑定」(static binding),也就是说,在编译期间就能决定运行时所应调用的函数。以下代码为例:

#import <stdio.h>
void printHello() {
printf("Hello, world!\n");
}
void printGoodbye() {
printf("Goodbye!\n");
}
void doTheThing(int type) {
if (type == 0) {
printHello();
} else {
printGoodbye();
}
return 0;
}

如果不考虑「内联」(inline),那么编译器在编译代码的时候就已经知道程序中有printHello与printGoodbye这两个函数了,于是会直接生成调用这些函数的指令(站在汇编的角度,call命令)。而函数地址实际上是与硬编码在占领之中的。

如果将上述代码写成下面这样,会如何呢?

#import <stdio.h>
void printHello() {
printf("Hello, world!\n");
} void printGoodbye() {
printf("Goodbye!\n");
} void doTheThing(int type) {
void (*fnc)();
if (type == 0) {
fnc = printHello;
} else {
fuc = printGoodbye;
}
fnc();
return 0;
}

这就是「动态绑定」(dynamic binding)!因为所要调用的函数知道运行期才能确定。编译器在这种情况下生成的指令与刚才的那个例子不同,在第一个例子(静态绑定)中,if与else语句里都有函数调用指令(汇编中的call命令);而在第二个例子(动态绑定)中,只有一个函数调用指令,不过待调用的函数地址无法硬编码之中,而是要在运行期读出来。

消息传递机制

在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全在runtime决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为一门真正的动态语言

给对象发生消息可以这样写:
id returnValue = [someObject messageName:parameter];

someObject是「接收者」(receiver),messageName叫做「选择子」(selector)。二者合起来称为「消息」(message)。

P.S:「选择子」和「方法」这两个名词经常交替使用,都是一个意思。

编译器看到此消息后,将其转换为一条标准的C语言函数调用,所调用的函数乃是「消息传递机制」中的核心函数,即鼎鼎有名的objc_msgSend,其原型可以在<objc/message.h>中找到:

id objc_msgSend(id self, SEL op, ...)

id returnValue = [someObject messageName:parameter];会被编译器翻译成如下形式:

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

在runtime,objc_msgSend会以及receiver(即上述对象someObject)和选择子类型来调用适当的方法。为了完成此操作,该方法需要在接收者所属的类中搜寻其「方法列表」(关于方法列表,详见methodLists),如果能找到与「选择子」匹配的方法,就调至其实现的代码。若找不到,那就沿着集成体系继续向上查找,等找到何时的方法之后再跳转。如果最终还找不到相匹配的方法,那就执行「消息转发」(message forwarding)操作,这会在下一篇博客中阐述。

如此看来,Objective-C在runtime调用一个方法似乎需要很多步骤。所幸的是,objc_msgSend会将匹配结果缓存在「快速映射表」(fast map)里面,每个类都有这样一块缓存,若是稍后还想该类发送与「选择子」相同的方法,那么查找起来就很快了。

P.S:上一篇博客《理解Objective-C Runtime(一)预备知识》中在介绍objc_class结构体时谈到了其中一个变量cache,但简单忽略飘过;其实,根据我的理解,这里的「快速映射表」所对应的就是objc_class中的变量cache。

诚然,即便有这种「快速映射表」机制,执行速度还是不如「静态绑定」。实际上,对于当前这种硬件平台,这点速度差根本不值一提。

<objc/message.h>中,除了objc_msgSend函数原型之外,还可以看到其他的objc_msgSendXXOO函数,这些方法的详细作用,《Effective Objective-C 2.0》item 11中有详细说明,本文就不赘述了。

只要理解了Objective-C的对象模型,理解「消息传递机制」还是非常容易的。

参考资料

  • 《Effective Objective-C 2.0》;

Objective-C Runtime(二)消息传递机制的更多相关文章

  1. Chrome插件开发入门(二)——消息传递机制

    Chrome插件开发入门(二)——消息传递机制   由于插件的js运行环境有区别,所以消息传递机制是一个重要内容.阅读了很多博文,大家已经说得很清楚了,直接转一篇@姬小光 的博文,总结的挺好.后面附一 ...

  2. Chrome 消息传递机制

    Chrome插件开发入门(二)——消息传递机制 Blog | Qiushi Chen 2014-03-31 9538 阅读 Chrome 插件 由于插件的js运行环境有区别,所以消息传递机制是一个重要 ...

  3. ios 消息传递机制

    引用文章 一.KVO 1.当对象中的某个属性值发生了改变,可以对这些值的观察者做出通知. 2.接受者(会接收到值发生改变的消息) 必须知道发送者(值将发生改变的那个对象). 3.接收者同样还需要知道发 ...

  4. Storm内部的消息传递机制

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 一个Storm拓扑,就是一个复杂的多阶段的流式计算.Storm中的组件 ...

  5. 我理解的Hanlder--android消息传递机制

    每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与Ac ...

  6. Handler消息传递机制

    引言: 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题. 为了解决这个问题,Android制定了一条简单的规则:只允许UI线程 ...

  7. Android异步消息传递机制源码分析

    1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...

  8. 安卓开发_深入理解Handler消息传递机制

    一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存 ...

  9. runtime消息转发机制

    Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ...

随机推荐

  1. 让Mac OS X下的终端像Linux那样拥有丰富多彩的颜色显示

    我们知道Linux下的命令行终端具有颜色回显功能,用ls命令查看目录或者文件,终端会以不同的颜色来区分:使用vim命令行编辑器打开脚本或其他源程序代码会以语法高亮模式显示.而Mac OS X下的终端却 ...

  2. Jetson TK1 四:重新安装系统(刷机)

    转载:http://blog.sina.com.cn/s/blog_bab3fa030102vk21.html Jetson TK1是NVIDIA基于Tegra K1开发的一块低成本开发板,板载一块T ...

  3. Java ListIterator 与 Iterator 异同

    一.概述 基于 fail-fast 机制,我们知道对于ArrayList等集合在迭代过程中是不可进行结构修改操作的,唯一能使用的结构修改操作只有Iterator接口中的remove()方法. 而jav ...

  4. Linux下&/jobs/fg/bg命令的使用(转)

    一.& 这个用在一个命令的最后,可以把这个命令放到后台执行. 二.[Ctrl]+[Z] 可以将一个正在前台执行的命令放到后台,并且暂停. 三.jobs 查看当前有多少在后台运行的命令. 四.f ...

  5. Intel Edision —— 上电、基本设置与系统初探

    前言 原创文章,转载引用务必注明链接.如有疏漏,欢迎斧正. Intel的文档其实挺清楚了,坛子上很多人把文档又详细复述一边,私以为一篇就够了其他的跟着文档走一遍也挺好的...俗一把使用过程顺手记录下来 ...

  6. 关于IP地址与MAC地址(网卡硬件地址)的区别小谈

    IP地址是指Internet协议使用的地址,而MAC地址是Ethernet协议使用的地址. IP地址与MAC地址之间并没有什么必然的联系,MAC地址是Ethernet NIC(网卡)上带的地址,为48 ...

  7. reorder-list——链表、快慢指针、逆转链表、链表合并

    Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do thi ...

  8. [unity3d]unity平台的预处理

    在开发中,特别是unity的跨平台中,我们常常会在各个平台游走,如安卓版,苹果版,PC版.......在此不同的平台上,有可能我们须要做不同的操作.然而我们就能够用unity的自带的平台宏定义方式来做 ...

  9. js中cookie的使用具体分析

                   JavaScript中的还有一个机制:cookie,则能够达到真正全局变量的要求. cookie是浏览器 提供的一种机制,它将document 对象的cookie属性提供 ...

  10. docker (2)---存储、网络(利用docker容器上线静态网站)

    一.docker底层依赖的核心技术 1.命名空间 (Namespaces) 2.控制组 (Control Groups) 3.联合文件系统 (Union File System) 4.Linux 虚拟 ...