怎么挽救unrecognized selector异常——消息转发机制
Posted 阿曌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎么挽救unrecognized selector异常——消息转发机制相关的知识,希望对你有一定的参考价值。
[obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj, @selector(foo));
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会:
Method resolution
objc运行时会调用+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则 ,运行时就会移到下一步,消息转发(Message Forwarding)。
Fast forwarding
如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步转发会创建一个NSInvocation对象,所以相对更快点。
Normal forwarding
这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
总而言之,我们可以在自己的类重写上述的方法,阻挡程序走到doesNotRecognizeSelector
抛异常。
github上有个demo可以供下载试验:_objc_msgForward_demo
下面分别来解释下各个函数的具体写法:
//第一调用
+ (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
NSLog(@"resolveInstanceMethod - %s", sel);
class_addMethod(self.class, sel, (IMP)imp, "@:");
[super resolveInstanceMethod:sel];
return YES;
id imp(id self, SEL _cmd)
return nil;
其中最重要的就是class_addMethod()
方法,前几个参数都比较好理解,给self.class
加一个名为sel
,实现为imp
的方法。
最后一个参数”@:”表示新加方法的样式,@
表示self
,:
表示_cmd
(即SEL),注意看正好对应imp的两个参数(id self, SEL _cmd)
。
class_addMethod
最后一个参数一定要加且至少要包含@:
,除此之外可以在前后加额外的信息,比如v@:@
表示函数体为void xxx(id self, SEL _cmd,NSString* str)
,放在最前面表示return的类型,v
表示void
,@
表示字符串类型。
其他字符对应可以看这里《Type Encodings》。
resolveInstanceMethod
有个不好的地方,就是会截断一些本不会造成unrecognized selector异常的消息发送,这也是我调试时发现的,原因还不得而知。
未完。。。
第三种方法会影响Lua的wax调用所以不能用
以上是关于怎么挽救unrecognized selector异常——消息转发机制的主要内容,如果未能解决你的问题,请参考以下文章
解决因为NSNull类型导致出现的unrecognized selector sent to instance问题
unrecognized selector send to instancd 快速定位
Xamarin.iOS unrecognized selector sent to class
unrecognized selector sent to class ShareSDK集成