runtime消息转发流程
从全局来看,消息转发机制共分为3大步骤:
1.Method resolution 方法解析处理阶段
2.Fast forwarding 快速转发阶段
3.Normal forwarding 常规转发阶段
那么如果想要不抛出unrecognized selector 的报错,也就需要从这3步里面来做补救了,我们一步一步来看如何在这3个阶段来进行补救。
第一步:Method resolution 方法解析处理阶段
如果调用了对象方法首先会进行+(BOOL)resolveInstanceMethod:(SEL)sel判断
如果调用了类方法 首先会进行 +(BOOL)resolveClassMethod:(SEL)sel判断
两个方法都为类方法,如果YES则能接受消息 NO不能接受消息 进入第二步
我们先调用一下对象方法
[self.test performSelector:@selector(testFunction)];
然后在resolveInstanceMethod进行补救,这里用到了我封装的一个Runtime工具类,这里暂时不做展开讲解,会在后续其他文章里面展开讲解,功能是对一个类添加一个方法。
+(BOOL)resolveInstanceMethod:(SEL)sel{//判断是否为外部调用的方法if([NSStringFromSelector(sel)isEqualToString:@"testFunction"]){/**
对类进行对象方法 需要把方法添加进入类内
*/[LMRuntimeTool addMethodWithClass:[selfclass]withMethodSel:sel withImpMethodSel:@selector(addDynamicMethod)];returnYES;}return[superresolveInstanceMethod:sel];}
下面我们再来调用一下TestMessage的类方法
[[TestMessage class]performSelector:@selector(testClassFunction)];
如果调用类方法需要在resolveClassMethod 进行补救判断
+(BOOL)resolveClassMethod:(SEL)sel{if([NSStringFromSelector(sel)isEqualToString:@"testClassFunction"]){/**
对类进行添加类方法 需要讲方法添加进入元类内
*/[LMRuntimeTool addMethodWithClass:[LMRuntimeTool getMetaClassWithChildClass:[selfclass]]withMethodSel:sel withImpMethodSel:@selector(addClassDynamicMethod)];returnYES;}return[superresolveClassMethod:sel];}
这里有一个需要特别注意的地方,类方法需要添加到元类里面,OC中所有的类本质上来说都是对象,对象的isa指向本类,类的isa指向元类,元类的isa指向根元类,根元类的isa指向自己,这样的话就形成了一个闭环。
[LMRuntimeTool getMetaClassWithChildClass:[self class]] 这个方法是用来获取本类的元类,对元类添加需要添加的方法。
经过上面两种类型的补救,果然对象方法和类方法都不在抛出异常了,并且打印了数据
2018-08-06 15:25:25.667572+0800 MessageForwardDemo[3599:949889] 动态添加类方法2018-08-06 15:25:25.667612+0800 MessageForwardDemo[3599:949889] 动态添加方法
第二步:Fast forwarding 快速转发阶段 (后面阶段都针对对象来处理,不考虑类方法)
如果在上一步的2个方法内返回的为YES则能接受消息 NO不能接受消息 进入第二步,我们先把上面方法内的处理方案注释掉,让消息转发进入第二步。
我们新创建一个BackupTestMessage类,里面声明和实现testFunction方法,用来当作备用响应者。
-(id)forwardingTargetForSelector:(SEL)aSelector{if([NSStringFromSelector(aSelector)isEqualToString:@"testFunction"]){return[BackupTestMessage new];}return[superforwardingTargetForSelector:aSelector];}
因为一个对象内部可能还有其他可能响应的对象,所以这个方法是转发SEL去对象内部的其他可以响应该方法的对象。
这里创建的一个BackupTestMessage的实例内定义的有testFunction方法,所以返回这个实例之后,果然不再报错了,并且根据打印也能看得出来走了BackupTestMessage这个类的实例方法
2018-08-06 15:27:43.234733+0800 MessageForwardDemo[3629:951288] 备用类的对象方法testFunction
已经让备用的对象去响应了TestMessage本身无法响应的一个SEL
作者:Leesim
链接:https://www.jianshu.com/p/fdd8f5225f0c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。