OC消息转发机制
2017-04-19 20:37
330 查看
OC方法调用发送消息,如果找不到对应名称的方法时,在发生Crash之前有三次机会进行补救。
动态方法解析
备用接受者
消息转发
这个方法返回值Bool类型,表示这个类能否新增一个方法处理这种情况。使用这种办法,需要实现写好对应的方法实现代码,在运行时的时候添加插入就好了。
这个方法常用来实现@dynamic 属性的setter getter 方法
e.g.
这种情况下调用setter 和 getter方法会发生crash
下面演示了用此方法实现@dynamic 属性
这样在调用setter和getter方法的时候会调用之前实现好的C语言方法,保证程序正常运行。
这个方法返回一个非空对象,这个对象会作为这个方法新的接受者,重新进行消息发送。
e.g.
新建一个TestObjB对象
然后在TestObjA 中实现下面方法
现在再进行一下测试:
会发现有打印输出:test
然后运行上次的测试代码,结果仍然可以打印输出:test
在这个阶段,会先调用
然后调用
注意: anInvocation 的初始化必须要 方法签名: NSMethodSignature
至此,三个可以拦截未知消息crash的步骤已经完成。如果都没有进行处理,运行时会调用NSObject的forwardInvocation:方法,这个方法的实现只是简单调用了doesNotRecognizeSelector:方法,它不会转发任何消息,直接抛出异常。
动态方法解析
备用接受者
消息转发
动态方法解析
对象在接收到未知的消息时,首先会调用所属类的类方法 +resolveInstanceMethod:(实例方法)或 者+resolveClassMethod:(类方法)。这个方法返回值Bool类型,表示这个类能否新增一个方法处理这种情况。使用这种办法,需要实现写好对应的方法实现代码,在运行时的时候添加插入就好了。
这个方法常用来实现@dynamic 属性的setter getter 方法
e.g.
// // TestObjA.h // test // @interface TestObjA : NSObject @property(nonatomic , strong) NSString *testStr; @end // // TestObjA.m // test // @implementation TestObjA @dynamic testStr; @end
这种情况下调用setter 和 getter方法会发生crash
TestObjA *obj = [[TestObjA alloc] init]; [obj setTestStr:@"haha"]; NSString *result = [obj testStr]; NSLog(result);
下面演示了用此方法实现@dynamic 属性
// // TestObjA.h // test // @interface TestObjA : NSObject @property(nonatomic , strong) NSString *testStr; @property (nonatomic, strong) NSString *storeStr; @end // // TestObjA.m // test // void dynamicStrSetter (id self,SEL _cmd, id value) { TestObjA *obj = (TestObjA *)self; obj.storeStr = (NSString *)value; } id dynamicStrGetter (id self,SEL _cmd) { TestObjA *obj = (TestObjA *)self; return obj.storeStr; } @implementation TestObjA @dynamic testStr; + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(setTestStr:)) { class_addMethod(self, sel, (IMP)dynamicStrSetter, "v@:@"); return true; }else if(sel == @selector(testStr)) { class_addMethod(self, sel, (IMP)dynamicStrGetter, "@@:"); return true; } return [super resolveClassMethod:sel]; }; @end
这样在调用setter和getter方法的时候会调用之前实现好的C语言方法,保证程序正常运行。
备用接受者
如果上一步没有实现,会继续调用下面的方法- (id)forwardingTargetForSelector:(SEL)aSelector
这个方法返回一个非空对象,这个对象会作为这个方法新的接受者,重新进行消息发送。
e.g.
新建一个TestObjB对象
// // TestObjB.h // test // @interface TestObjB : NSObject - (void)testFunc; @end // // TestObjB.m // test // @implementation TestObjB - (void)testFunc { NSLog(@"test"); } @end
然后在TestObjA 中实现下面方法
- (id)forwardingTargetForSelector:(SEL)aSelector { return [[TestObjB alloc] init]; }
现在再进行一下测试:
TestObjA *obj = [[TestObjA alloc] init]; [obj performSelector:@selector(testFunc) withObject:nil];
会发现有打印输出:test
消息转发
将上面第二步TestObjA 中的代码删除,添加一下代码:- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *signature = [super methodSignatureForSelector:aSelector]; if (!signature) { if ([TestObjB instancesRespondToSelector:aSelector]) { signature = [TestObjB instanceMethodSignatureForSelector:aSelector]; } } return signature; } - (void)forwardInvocation:(NSInvocation *)anInvocation { if ([TestObjB instancesRespondToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:[[TestObjB alloc] init]]; } }
然后运行上次的测试代码,结果仍然可以打印输出:test
在这个阶段,会先调用
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector生成一个方法签名
然后调用
- (void)forwardInvocation:(NSInvocation *)anInvocation方法进行消息转发。
注意: anInvocation 的初始化必须要 方法签名: NSMethodSignature
至此,三个可以拦截未知消息crash的步骤已经完成。如果都没有进行处理,运行时会调用NSObject的forwardInvocation:方法,这个方法的实现只是简单调用了doesNotRecognizeSelector:方法,它不会转发任何消息,直接抛出异常。
相关文章推荐
- OC消息转发机制 整理
- OC 的消息转发机制 (新博客 很久不更新啊 忙着找工作)
- 关于OC中消息转发机制的理解以及在项目中的实际应用
- OC高效率52之理解消息转发机制
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
- iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制
- oc消息转发机制
- oc的消息传递机制与消息转发机制
- oc消息转发机制
- OC-消息转发机制
- OC学习Runtime之消息传递,消息转发机制
- oc消息转发机制本质
- 关于OC中消息转发机制的理解以及在项目中的实际应用
- 利用OC的消息转发机制实现多重代理
- iOS runtime探究(二): 从runtime开始深入理解OC消息转发机制
- oc消息转发机制
- 利用OC的消息转发机制实现多重代理
- RunTime(消息机制) + RunTime(消息转发)
- Objective-C runtime之消息转发机制(三)
- RxSwift Runtime分析(利用OC消息转发实现IOS消息拦截)<原理同ReactiveCocoa>