OC消息转发
来源:互联网 发布:java api接口编写实例 编辑:程序博客网 时间:2024/06/04 19:49
关于消息转发已经是个烂话题,可以搜索下别人的文章
如果没有搜索,这里也会先简单介绍下消息转发概念和过程
简单介绍消息转发过程
oc的方法调用过程是一次消息发送的过程
当类的方法表里没有消息中的选择子时,会有一个消息转发的阶段
第一阶段:可以在该阶段动态添加方法
类方法需要覆盖resolveClassMethod
实例方法需要覆盖resolveInstanceMethod
这两个方法返回前可以为未找到的选择子动态加入方法
+ (BOOL)resolveClassMethod:(SEL)sel{//dynamic add method then return YES//class_addMethod(Class cls, SEL name, IMP imp, const char *types); //return YES;return NO;}- (BOOL)resolveInstanceMethod:(SEL)sel{//dynamic add method then return YESreturn NO;}
第二阶段:交给其他对象处理
- (id)forwardingTargetForSelector:(SEL)aSelector{ //返回一个可以处理选择子的对象 // return someObjCanHanleThisSelector; return [super forwardingTargetForSelector:aSelector];}
当然这只是字面上的意思,依然可以动态添加方法后返回自己
第三阶段:拥有完整消息的转发处理
NSInvocation进入头文件可以查看到,这个对象包含并且可以设置一次消息发送的所有要素
- (void)forwardInvocation:(NSInvocation *)anInvocation{ //anInvocation can set anything [anInvocation invoke];}
还需要配合方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
如果你是新手还不知道如何动态加入方法,一个小提示class_addMethod(Class cls, SEL name, IMP imp, const char *types);xcode进入头文件会有一堆类似的方法
科普时间:
Selector
简单点说是一个方法的名字,或者说是在某个对象寻找方法实现时的索引
IMP
一个接受调用者、选择子、方法入参并返回id的函数指针,看下代码:
typedef id (*IMP)(id, SEL, ...);
可以理解为一个方法真正的实现
NSMethodSignature
名字这么玄乎,实际是对方法参数的一个包装,例如可以调用
[NSMethodSignature signatureWithObjCTypes:SomeTypeEncoding]
NSInvocation
这是一个消息调用的封装,拥有一次消息发送的所有信息,具体可进入头文件
那么问题来了,如果不希望再看到unrecognized selector...报错怎么办
思路就是让这个消息转发能安全走完(废话- -)
兜住实例方法
这里有一片很仔细的文章,介绍了如何兜住一个实例的方法调用
简单概括下,就是重写某一转发方法,用第三者对象去动态添加来保证完整的转发。
或者load时替换掉某一转发方法再去动态添加。
在上面很仔细的文章里也介绍了消息转让前的方法的寻找过程,这个过程就是下面代码往里走,如果需要验证这个过程,这里简单贴个入口(具体实现有点长,可以进这篇文章底部去下载源码):
IMP class_getMethodImplementation(Class cls, SEL sel){ IMP imp; if (!cls || !sel) return nil; imp = lookUpImpOrNil(cls, sel, nil, YES/*initialize*/, YES/*cache*/, YES/*resolver*/); // Translate forwarding function to C-callable external version if (!imp) { return _objc_msgForward; } return imp;}
那么如何添加类方法?
实验环境
如果需要考虑可行性,可以先看看这里
为了完成这个实验,先写了篇oc里对象、类、元类的简单介绍。
简单贴一下实验环境:
- (void)startTest{ [[self class]performSelector:@selector(goFindNonexistedClassMethod) withObject:nil afterDelay:0];}+ (void)nonexistedClassMethodSaver { NSLog(@"recognized selector bla bla");}+ (BOOL)resolveClassMethod:(SEL)sel{ if ([NSStringFromSelector(sel) isEqualToString:@"goFindNonexistedClassMethod"]) { //do some thing return YES; } return YES;}为什么别的转发方法不贴了?因为resolveClassMethod返回NO就崩溃了
思路就是需要在注有"do some thing"的地方加入方法
class_addMethod
博文上面有提到这个方法class_addMethod(Class cls, SEL name, IMP imp, const char *types),它可以为这个类的实例添加方法,实例(至少翻译过来是这样)
那么但需要为类自己添加方法时可不可以用这个方法呢?(这么问肯定是可以,怎么知道的?崩溃多几次)
在多次尝试后,找到了答案,有一个启发来源于巧大大的一篇文章(博客上没找到,去公众号找到了)
里面提到了关于NSObject哪些方法定义由类定义,哪些方法由元类定义。文中提到NSObject类中定义了实例的init等方法,元类中定义了+(id)alloc,+(void)load灯方法
那么大胆尝试下 给这个函数来一一填入参数
给class传入对象的元类
找到IMP [self methodForselector:@selector(nonexistedClassMethodSave)]
找IMP时遇过的小坑错误的调用了class_getMethodImplemention(cls,SEL),这个函数会找实例方法里有没有对应的IMP,当然没有,然后进入-resolveInstanceMethod:,套用NSObject里方法层级的定义,这里传入metaclass试试,结果可行(当然,‘套用’俩字的背后是代码实现,去metaclass的方法列表找到了类方法)
types method_getTypeEncoding(class_getClassMethod(self)) (为什么传self,这个函数会先找metaclass然后搜索)
最后把上面的参数一一填入,结果成功。
P.S.
欢迎讨论!
最近上架了一个app,可以点击这里查看,实现起来并不难,在体验后如果有需要可以在以后的博文里介绍下实现的心路历程,欢迎反馈
0 0
- oc消息转发机制
- OC-消息转发机制
- oc消息转发机制
- OC的消息转发
- OC消息转发机制
- oc消息转发机制
- OC消息转发
- OC消息转发机制
- OC 中消息转发的流程图
- OC阅读笔记四:消息转发
- 在 OC 中实现消息的转发
- OC 消息转发和重定向
- OC 异常消息转发处理流程
- OC学习Runtime之消息传递,消息转发机制
- oc的消息传递机制与消息转发机制
- 利用OC的消息转发机制实现多重代理
- <<Effective OC>>读书笔记 --- 第十二条 理解消息转发
- OC Runtime学习及运行时消息转发机制
- 2017年度个人计划
- Android 广告轮播(伪无限循环)
- API网关Ocelot 使用Polly 处理部分失败问题
- REF游标的用法
- JavaScript将url地址快速变成键值对的JavaScript对象形式
- OC消息转发
- 安卓 广播
- jsp九大内置对象详解
- 一次查询性能提高40倍的经历
- ASP.NET MVC 5 (五)c#的lambda表达式、LinQ和Async异步处理
- java.lang.OutOfMemoryError:GC overhead limit exceeded
- 小鑫の日常系列故事(七)——小纸条 (sdut oj)
- Objective-C NSDictionary
- React Native Awesome(汇聚知识,分享精华)