消息转发
前文介绍了进行一次发送消息会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索直到继承树根部(通常为NSObject),如果还是找不到并且消息转发都失败了就回执行 doesNotRecognizeSelector: 方法报 unrecognized selector 错。
但在抛出异常之前,还有 三次机会 按以下顺序让你拯救程序:
- Method Resolution 动态方法解析
- Fast Forwarding 备用接收者
- Normal Forwarding 完整消息转发
消息转发流程简图:
动态方法解析 Method Resolution
首先,OC运行时会调用 +resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。
实现一个动态方法解析的例子如下:
| 1 | @interface Message : NSObject | 
如果创建Message对象并调用sendMessage:方法:
| 1 | Message *message = [Message new]; | 
控制台会打印以下信息:
| 1 | normal way : send message = App Dev Club | 
但现在我将原来sendMessage:方法实现给注释掉,覆盖resolveInstanceMethod:方法:
| 1 | 
 | 
控制台就会打印以下信息:
| 1 | method resolution way : send message = App Dev Club | 
注意到上面代码有这样一个字符串"v@*"("v@:"),它表示方法的参数和返回值,详情参考 Type Encodings 
可以看到虽然没有实现sendMessage:这个函数,但是我们通过class_addMethod动态添加函数,并执行这个函数的IMP。从结果看,成功实现了。
如果resolve方法返回 NO ,运行时就会移到下一步:forwardingTargetForSelector。
备用接收者 Fast Forwarding
如果目标对象实现- forwardingTargetForSelector:方法,系统就会在运行时调用这个方法,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。否则,就会继续Normal Fowarding。
继续上面Message类的例子,将sendMessage:和resolveInstanceMethod:方法注释掉,然后添加forwardingTargetForSelector:方法的实现:
| 1 | 
 | 
此时还缺一个转发消息的类MessageForwarding,这个类的设计与实现如下:
| 1 | @interface MessageForwarding : NSObject | 
此时,控制台会打印以下信息:
| 1 | fast forwarding way : send message = App Dev Club | 
这里叫Fast,是因为这一步不会创建NSInvocation对象,但Normal Forwarding会创建它,所以更快点。
完整消息转发 Normal Forwarding
如果没有使用Fast Forwarding来消息转发,则唯一能做的就是启用完整的消息转发机制了.
首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。
如果-methodSignatureForSelector:返回 nil ,Runtime则会发出 -doesNotRecognizeSelector:消息,程序这时也就crash掉了,并抛出 unrecognized selector sent to instance 异常信息。
如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送 -forwardInvocation:消息给目标对象。
实例:
| 1 | 
 | 
三种方法的选择
Runtime提供三种方式来将原来的方法实现代替掉,那该怎样选择它们呢?
- Method Resolution:由于Method Resolution不能像消息转发那样可以交给其他对象来处理,所以只适用于在原来的类中代替掉。 
- Fast Forwarding:它可以将消息处理转发给其他对象,使用范围更广,不只是限于原来的对象。 
- Normal Forwarding:它跟Fast Forwarding一样可以消息转发,但它能通过 - NSInvocation对象获取更多消息发送的信息,例如:target、selector、arguments和返回值等信息。