1. 消息由接收者、选择子及参数构成。给某对象“发送消息”(invoke a message)也就相当于在该对象上“调用方法”(call a method);

2. 发给某对象的全部消息都由“动态消息派发系统”(dynamic message dispatch system)来处理,该系统会查出对应的方法,并执行其代码;


3. C语言是使用“静态绑定”(static binding),在编译器就能决定运行时所应调用的函数。

如:

[objc] view plaincopyEffective Objective-C 2.0 编写高质量iOS与OS X代码 objc_msgSend 的作用_returnEffective Objective-C 2.0 编写高质量iOS与OS X代码 objc_msgSend 的作用_dynamic_02

  1. void printA()  

  2. {     printf(“helloA”);     }  

  3.   

  4. void printB()  

  5. {     printf(“helloB”);     }  

  6.   

  7. void do(int type){  

  8.      if(type ==1)  

  9.           printA();  

  10.      else       

  11.           printB();  

  12.      return 0;  

  13. }  



不考虑内联,那么编译器在编译代码的时候就已经知道程序中有两个函数了。于是会直接生成调用这些函数的指令。

而函数地址实际上是硬编码在指令之中的。

若是以下代码:

[objc] view plaincopyEffective Objective-C 2.0 编写高质量iOS与OS X代码 objc_msgSend 的作用_returnEffective Objective-C 2.0 编写高质量iOS与OS X代码 objc_msgSend 的作用_dynamic_02

  1. void printA()  

  2. {     printf(“helloA”);     }  

  3.   

  4. void printB()  

  5. {     printf(“helloB”);     }  

  6.   

  7. void do(int type){  

  8.      void (*fnc)();  

  9.      if(type ==1)  

  10.           fnc = printA();  

  11.      else       

  12.           fnc = printB();  

  13.      fnc();  

  14.      return 0;  

  15. }   



此时就要使用“动态绑定”(dynamic binding),因为所要调用的函数知道运行期间才能确定。待调用的函数地址无法硬编码在指令中,而是要通过运行期读取出来。


OC的消息机制:

id returnValue = [someObject messageName:parameter];

转化为C语言函数调用:

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

objc_msgSend函数会依据接收者与选择子的类型来调用适当的方法。

该方法需要在接收者所属的类中寻找“方法列表”(list of methods)。

① 如果能找到与选择子名称相符的方法,就跳转至实现代码。

② 如果找不到,那就沿着继承体系继续向查找。等找到合十的方法后再跳转。

③ 如果最终还是找不到,那就执行“消息转发”(message forwarding)操作。

另外:objc_msgSend 会将匹配的结果缓存再“快速映射表”(fast map)。每个类都有这样一块缓存。


<return_type> Class_selector(id self, SEL_CMD,…)

每个类里都有一张表格,其中的指针都会指向这种函数,而选择子的名称则是查表时所用的“键”。

利用“尾调用优化”(tail-call optimization,尾递归优化),令“跳至方法实现”这一操作变得更简单些。

如果某函数的最后一项操作是调用另外一个函数,那么就可以运用“尾调用优化”计数。编译器会生成跳转至另一个函数所需要的指令码,而且不会向调用堆栈中推入新的“栈帧”(frame stack)。

只有当某函数的最后一个操作仅仅是调用某其他函数而不会将其返回值另做他用时,才能执行“尾调用优化”。

这项优化对objc_msgSend 非常重要。若不优化,可能会过早的“栈溢出”。

所以再调试的时候,栈“回溯”(backtrace)信息中总是出现objc_msgSend.