正常情况下,要想让对象能理解某条消息,那么我们必须以程序码实现出对应的方法才行。但是,在编译期间,向对象发送了一个未实现的消息,这并不会报错,因为在运行时可以继续添加方法。
如果向对象传递了一个未知的消息,而对象没法在自己所属的类中、父类......乃至根类中,找到对应的消息实现方法,就会报错闪退。身为程序员,crash是决定不能接受的啊。遇到这种问题,除了坐等crash,难道就没有别的法子了?这就是我们今天要了解的内容。
其实,在对象没法处理未知消息、程序crash之前,我们还有三个补救的机会,这就是传说中的消息转发机制。
消息转发,分四个步骤。
步骤一:动态方法解析。+(BOOL)resolveInstanceMethod:(SEL)name;
先征询对象所属的类,看其是否能动态添加方法,以处理当前这个未知的消息。如果能,那么消息转发结束。如果不能,进入步骤二。
步骤二:备援接收者。- (id)forwardingTargetForSelector:(SEL)aSelector;
再次征询对象所属的类,看其是否能找到别的对象来处理这个消息,说白了,就是打算找个有能力的人,把这个烫手山芋扔给那个人来处理。同样的道理,如果能找到接盘侠,那么消息转发结束。如果不能,那么进入步骤三。
步骤三,消息重定向。- (void)forwardInvocation: (NSInvocation*)invocation;
又称为完整的消息转发机制。到了这里,runtime 就会把与消息有关的全部细节,都分装到NSInvocation 对象中,再给消息接受者最后一个机会,让其设法解决当前还未处理的这条消息。即对象调用 forwardInvocation: 方法,如果不能处理就会调用父类的相关方法,一直到NSObject的这个方法,如果NSObject都无法处理就会调用doesNotRecognizeSelector: 方法抛出异常,程序crash。
有个流程图,解释的很清楚:
1457495-8ee6afef466e6177.jpg
最后,关于步骤二,其实可以用来模拟“多重继承”。比如说,有一个a对象,它的内部还有其他一系列其他的对象。那么a对象可以利用步骤二,让其他内部对象去处理某个消息,而在外部看来,以为是a对象本身去处理的,感觉跟类簇差不多?
但有个点需要注意,步骤二中,我们是没法去修改这个消息的内容的。
最后的最后,步骤三,可以修改消息的内容。运行时在调用- (void)forwardInvocation:(NSInvocation *)anInvocation之前,会先调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法来获取这个选择子的方法签名,然后在 -(void)forwardInvocation:(NSInvocation *)anInvocation 方法中我们就可以通过anInvocation拿到相应信息做处理。具体的就不展开了,自行谷歌。
网友评论