您现在的位置是:首页 > 文章详情

iOS消息转发小记

日期:2018-05-30点击:537

消息转发流程图

img_58f41d9c87a7b5c0c801e5cda7e8a6aa.jpe
15277558865032.jpg

如果类接收到无法处理的消息,会触发消息转发机制,一共有三个步骤,接受者在每一步中均有机会处理消息。步骤越往后,处理消息的代价就越大,所以最好再第一步就处理完。

第一道防线

在类里面实现两个方法来处理未知消息。执行动态方法解析之前,先会判断是否曾经有动态解析。

  • resolveInstanceMethod:处理实例方法
  • resolveClassMethod:处理类方法

我们来看个Demo,先看调用方代码

 TestA *testA = [[TestA alloc] init]; [testA instanceMethod]; [TestA classMethod]; 

再来看看TestA的定义。

// TestA.h @interface TestA : NSObject - (void)instanceMethod; + (void)classMethod; @end // TestA.m @implementation TestA - (void)newInstanceMethod { NSLog(@"newInstanceMethod"); } + (void)newClassMethod { NSLog(@"newClassMethod"); } + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(instanceMethod)) { // 动态添加方法newInstanceMethod Method method = class_getInstanceMethod([self class], @selector(newInstanceMethod)); IMP imp = method_getImplementation(method); class_addMethod([self class], sel, imp, method_getTypeEncoding(method)); // 成功处理,消息转发机制结束,调用newInstanceMethod return YES; } // 不能处理,进入第二步 return [super resolveInstanceMethod:sel]; } + (BOOL)resolveClassMethod:(SEL)sel { if (sel == @selector(classMethod)) { // 动态添加方法newClassMethod Method method = class_getInstanceMethod(object_getClass(self), @selector(newClassMethod)); IMP imp = method_getImplementation(method); class_addMethod(object_getClass(self), sel, imp, method_getTypeEncoding(method)); // 成功处理,消息转发机制结束,调用newClassMethod return YES; } // 不能处理,进入第二步 return [super resolveClassMethod:sel]; } @end 

TestA中头文件定义了两个方法,但是没有实现,如果不用消息转发机制处理异常,会导致crash,log想必大家应该很熟悉

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestA funcA]: unrecognized selector sent to instance 0x6040000125c0'

实例方法存储在类对象,类方法存储在元类对象,在调用class_addMethod时,第一个参数需要注意。

第二道防线

第二道防线依赖一个函数forwardingTargetForSelector

// 类方法 //+ (id)forwardingTargetForSelector:(SEL)aSelector { // //} - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(instanceMethod)) { // 消息转发给TestB实例 return [TestB new]; } // 消息转发失败,进入下一步 return nil; } // TestB.m - (void)instanceMethod { NSLog(@"instanceMethod"); } 

第三道防线

第三道防线有两步

  1. 调用methodSignatureForSelector,获取新的方法签名(返回值类型,参数类型)
  2. 调用forwardInvocation,转发消息,
// 方法签名(返回值类型,参数类型) // 类方法减号改为加号 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *signature = [TestB instanceMethodSignatureForSelector:aSelector]; return signature; } // NSInvocation封装了方法调用,包括:方法调用者、方法名、方法参数 // anInvocation.target 消息接受者 // anInvocation.selector 函数名 // [anInvocation getArgument:NULL atIndex:0]; 获取参数 // 类方法减号改为加号 - (void)forwardInvocation:(NSInvocation *)anInvocation { [anInvocation invokeWithTarget:[TestB new]]; } 
原文链接:https://yq.aliyun.com/articles/654880
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章