Objective-C 消息机制

开头

使用消息结构而非函数调用

使用消息结构的语言,其运行时执行的代码由运行时的具体环境来决定,典型的如OC。而使用函数调用的语言,典型如C等,则是由编译器决定的。
简单的两个特点:

  1. 利用虚方法表来实现多态。
  2. 运行时的检查,OC的重要工作都由运行期的组件来完成而非编译器。

在运行时,消息和方法通过objc_msgSend进行动态绑定。

此时存在方法调用[receiver mssage]

通过此调用整个逻辑转化objc_msgSend(receiver, selector)函数,该函数的参数实际是一个不定参数,其形式如objc_msgSend(receiver, selector,arg1,arg2,…)。一般的还会传入self和_cmd,编译器将会在编译时将这两个参数传入到函数中。


动态绑定

在前文中,提到了了消息机制实际是基于objc_msgSend这个函数的。但是在这个函数中如何实现动态绑定呢?
当[Object message]时,编译器会将这个方法调用转换成objc_msgSend函数

该函数会根据Object的isa指针,找到其所属的类,然后在其类中的方法列表中查找这个message对应的@selector

如果在这个类中没有找到则会遍历其父类,直到NSObject为止。一般的,OC中的类都是NSObject的子类。如果到NSObject都仍未找到则会调用_objc_msgForward函数,该函数用于消息转发。


关于消息转发

  1. 先征询接收者,检查是否存在的动态添加。
  2. 当运行完第1步时则不再检查,而查看其他对象是否有处理,如果没有处理则会启动完整的转发机制,封装到NSInvation中。

完整的消息转发机制如下图
消息转发图

  1. resolveInstanceMethod 方法时,允许用户为类动态添加方法,如果此方法捕获到相应的实现则会进行调用。否则进行下一个方法调用。
  2. forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接转发给它。如果返回了nil,继续下面的动作。
  3. forwardInvocation方法,实际是处理methodSignatureForSelector(该方法将尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常)得到的签名,该签名被包装成Invocation传入,forwardInvocation方法中进行处理。如果处理失败则该消息将得不到处理。