当一个对象接收到无法解读的消息时,就会启动消息转发(message forwording)机制。coder可经由此过程告诉对象应该如何处理位置消息。例如给某一个对象实例发送了一个该对象不存在的方法,最后就会crash掉。
-[NSCFDictionary indexOfObject:]: unrecognized selector sent to instance 0x233300555
进行一次发送消息会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索知道继承树根部(通常为NSObject),如果还是找不到并且消息转发都失败了就回执行doesNotRecognizeSelector:方法报unrecognized selector错。那么消息转发到底是什么呢?接下来将会逐一介绍最后的三次机会。
消息转发分为两大阶段。
第一阶段先询问接受对象所属的类是否能够动态添加方法,以处理这个unknown selector,这叫做动态方法解析(dynamic method resolution).
第二阶段设计完整的消息转发机制(full forwording mechanism)。
如果在运行期,系统已经把第一阶段执行完了,那么接受者自己就无法再以动态添加方法的方式来响应包含该unknown selector的消息了。此时,运行期系统会请求接受者以
其他手段来处理与消息相关的方法调用。这里又分为两小步:
2.1 首先,接受者查看是否有其他对象能处理这条消息,若有,则运行时系统会将消息转给那个对象,一切如常。
2.2 若没有可替代的接受者(replacement receiver),则启动完整的消息转发机制,运行时系统会把与消息有关的全部细节都封装到NSInvocation对象中,再给接受者最后一次机会,令其设法解决当前还未处理的消息。
所属类动态方法解析
//实例方法使用这个 +(BOOL)resolveInstanceMethod:(SEL)sel;
//类方法使用这个 +(BOOL)resolveClassMethod:(SEL)sel;
如果上述返回NO的话,就是不做动态方法解析,就会进行到下一步
备援接收者
当对象所属类不能动态添加方法后,runtime就会询问当前的接受者是否有其他对象可以处理这个未知的selector,相关方法声明如下:
- (id)forwardingTargetForSelector:(SEL)aSelector;
该方法的参数就是那个未知的selector,这是一个实例方法,因为是询问该实例对象是否有其他实例对象可以接收这个未知的selector,如果没有就返回nil,可以自行实验。
消息重定向
当没有备援接收者时,就只剩下最后一次机会,那就是消息重定向。这个时候runtime会将未知消息的所有细节都封装为NSInvocation对象,然后调用下述方法:
- (void)forwardInvocation: (NSInvocation*)invocation;
调用这个方法如果不能处理就会调用父类的相关方法,一直到NSObject的这个方法,如果NSObject都无法处理就会调用doesNotRecognizeSelector:方法抛出异常。
下图是整个消息转发机制