iOS的消息转发机制详解
来源:互联网 发布:php 查看网页源代码 编辑:程序博客网 时间:2024/06/14 00:37
iOS
开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法;或者,还有另外的方法,由于Objective-C
是一门动态语言,我们也可以在运行期再给类添加该方法,一样可以解决该问题,而这就涉及了类的消息转发机制。
本文就主要来介绍一下iOS
系统的消息转发机制,探究一下在调用一个方法时,如果本类中没有该方法时,对象究竟是如何进行消息转发的,来避免程序抛出异常。
异常现象
当调用的对象方法不存在,即使经过消息转发也不存在时,就会抛出下面的异常
-[Teacher playPiano]: unrecognized selector sent to instance 0x6000000114c0 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Teacher playPiano]: unrecognized selector sent to instance 0x6000000114c0'
解决办法
针对上述的异常问题,最简单的方法就是直接在类中添加playPiano
方法,或者在其继承树中添加该方法,均可以解决该问题,所以这种方法再次不再赘述,下面介绍一下如何利用消息转发机制解决该问题。
消息转发是在运行时进行的,大致分为两个阶段:第一阶段是先检查接收者,看是否能通过runtime
动态添加一个方法,来处理这个unknown selector
的消息;第二阶段就是完整的消息转发机制
,首先会先查看有没有其它对象能够处理该消息,如果没有,就把该消息的全部信息封装到NSInvocation
对象中,看那个对象能否处理,如果还无法处理,就查看继承树中的类是否能够处理该消息,如果到NSObject
之前都无法处理该消息,那么最后就会调用NSObject
类的doesNotRecognizeSelector
方法来抛出异常,表明调用的方法不存在。
1.动态方法解析
对象在收到无法处理的消息时,会调用下面的方法,前者是调用类方法时会调用,后者是调用对象方法时会调用
// 类方法专用+ (BOOL)resolveClassMethod:(SEL)sel// 对象方法专用+ (BOOL)resolveInstanceMethod:(SEL)sel
在该方法中,需要给对象所属类动态的添加一个方法,并返回YES
,表明可以处理
+ (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *method = NSStringFromSelector(sel); if ([@"playPiano" isEqualToString:method]) { /** 添加方法 @param self 调用该方法的对象 @param sel 选择子 @param IMP 新添加的方法,是c语言实现的 @param 新添加的方法的类型,包含函数的返回值以及参数内容类型,eg:void xxx(NSString *name, int size),类型为:v@i */ class_addMethod(self, sel, (IMP)playPiano, "v"); return YES; } return NO;}
2.备援接受者
经历了第一步后,如果该消息还是无法处理,那么就会调用下面的方法,查询是否有其它对象能够处理该消息
- (id)forwardingTargetForSelector:(SEL)aSelector
在这个方法里,我们需要返回一个能够处理该消息的对象
- (id)forwardingTargetForSelector:(SEL)aSelector{ NSString *seletorString = NSStringFromSelector(aSelector); if ([@"playPiano" isEqualToString:seletorString]) { Student *s = [[Student alloc] init]; return s; } // 继续转发 return [super forwardingTargetForSelector:aSelector];}
3.完整的消息转发
经历了前两步,还是无法处理消息,那么就会做最后的尝试,先调用methodSignatureForSelector:
获取方法签名,然后再调用forwardInvocation:
进行处理,这一步的处理可以直接转发给其它对象,即和第二步的效果等效,但是很少有人这么干,因为消息处理越靠后,就表示处理消息的成本越大,性能的开销就越大。所以,在这种方式下,会改变消息内容,比如增加参数,改变选择子等等。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector- (void)forwardInvocation:(NSInvocation *)anInvocation
下面是改变选择子的例子,比如我们直接调用的是playPiano
方法,最后转发给了traval:
方法,完整实例参考:MsgSendDemo
// 完整的消息转发- (void)travel:(NSString*)city{ NSLog(@"Teacher travel:%@", city);}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ NSString *method = NSStringFromSelector(aSelector); if ([@"playPiano" isEqualToString:method]) { NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"]; return signature; } return nil;}- (void)forwardInvocation:(NSInvocation *)anInvocation{ SEL sel = @selector(travel:); NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"]; anInvocation = [NSInvocation invocationWithMethodSignature:signature]; [anInvocation setTarget:self]; [anInvocation setSelector:@selector(travel:)]; NSString *city = @"北京"; // 消息的第一个参数是self,第二个参数是选择子,所以"北京"是第三个参数 [anInvocation setArgument:&city atIndex:2]; if ([self respondsToSelector:sel]) { [anInvocation invokeWithTarget:self]; return; } else { Student *s = [[Student alloc] init]; if ([s respondsToSelector:sel]) { [anInvocation invokeWithTarget:s]; return; } } // 从继承树中查找 [super forwardInvocation:anInvocation];}
iOS
的消息转发机制给我们提供了更多的选择,来保证消息的正常传递,而了解这些具体的实现方法,则可以让我们的程序更加的健壮。
参考资料
iOS消息转发机制详解
iOS 消息转发机制(VN的逃生之路)
NSMethodSignature和NSInvocation的用法
使用NSMethodSignature和NSInvocation实现消息转发
- iOS的消息转发机制详解
- iOS消息转发机制详解
- Ios的消息转发机制
- iOS的消息转发机制
- IOS消息转发机制
- iOS消息转发机制
- ios消息转发机制
- IOS 消息转发机制
- iOS消息转发机制
- iOS消息转发机制
- ios 消息转发机制
- ios-消息转发机制
- IOS消息传递转发机制
- iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)
- 【iOS沉思录】Objective-C语言消息传递机制三道防线:消息转发机制详解
- iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)
- 通过Runtime探讨iOS的消息转发机制
- runTime 的消息转发机制
- iCloud in iOS 11 FAQ: Everything you need to know!
- 学习OPENCV(一)
- 23种设计模式(13):迭代器模式
- php 搜索分页接口
- 无线网络覆盖
- iOS的消息转发机制详解
- bool
- hibernate的annotation用法之----单向/多向的多对一映射
- JEESZ分布式框架简介--技术介绍文档
- java学习中关于静态和非静态
- 素数筛
- linux-2-环境变量(未完)
- SSH普通用户HostbasedAuthentication配置
- POJ 1860 Currency Exchange 货币兑换★