小萝莉说Crash(二): Unrecognized selector xxx 之 ForwardInvocation

2017-06-05 FanFar

本文来自于腾讯 Bugly,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,非经作者同意,请勿转载。

在上篇的分享中,小萝莉给大家介绍了一个入门必现的应用崩溃问题 —— Unrecognized selector sent to instance xxx,通过分析其出现的主要场景,给大家提出了一些避免出现此类问题的建议。然而,古语有云:“斩草不除根,则必留后患”(感觉好邪恶的样子,嘿嘿嘿)。 今天,小萝莉就要给大家分享规避此类问题的终极利器 —— ForwardInvocation(消息重定向)。

一、崩溃问题产生的过程

知识回顾Objective-C的方法调用实际是一种消息传递,当向Objective-C对象发送一个消息时,Runtime如果在当前类及父类中找不到此selector对应的方法,在执行一个消息转发的流程后,最终产生一个崩溃,即前面提到的Unrecognized selector sent to instance xxx问题。(公众号回复“2001”,回顾“小萝莉说Crash(一):Unrecognized selector sent to instance xxx”) 实际上,应用出现Unrecognized selector sent to instance xxx问题是在一个消息传递转发流程执行完毕后,实在是找不到可以接收消息的对象时,才会抛出一个崩溃错误。(让我处理这消息,真心做不到啊=_=)

1. “臣妾”真的做不到 —— 消息转发流程Objective-C的方法调用的消息传递过程按照如下流程执行:

消息转发过程的关键方法

  • 动态方法解析

向当前类发送resolveInstanceMethod:消息,检查是否动态向类添加了方法,如果返回YES,则系统认为方法已经被添加,则会重新发送消息。

  • 快速消息转发

检查当前类是否实现forwardingTargetForSelector:方法,若实现则调用,如果方法返回值为非nil或非self的对象,则向返回的对象重新发送消息。

  • 标准消息转发

Runtime发送methodSignatureForSelector:消息获取selector对应方法的签名,如果有方法签名返回,则根据方法签名创建描述消息的NSInvocation,向当前对象发送forwardInvocation:消息,如果没有方法签名返回,即返回值为nil,则向当前对象发送doesNotRecognizeSelector:消息,应用崩溃退出。

二、崩溃问题规避方法

从前文提到的消息转发的流程可以知道,当向某个对象发送消息,Runtime在当前类和父类中都找不到对应方法实现时,应用并不会立即崩溃退出,而是先执行一个完整的消息转发流程才会结束。 这也就给了我们去修正问题的机会。 1. 有准备才能抓住机会 —— 实现动态加载方法 如果你有意识到此类崩溃问题,并期望可以在运行时有机会添加缺失的方法,那么你就可以通过实现NSObject的resolveInstanceMethod:方法,并利用class_addMethod方法动态添加函数。如下:

2. 再次改过自新的机会 —— 快速消息转发如果你没有采用动态加载方法处理此类问题,即不实现NSObject的resolveInstanceMethod:方法,你也可以实现NSObject的 forwardingTargetForSelector:方法,以声明一个新的类对象来处理这个消息。 如下:

3. 机不可失,失不再来 —— 标准消息转发如果你只想规避此类问题,那你可以通过实现NSObject的methodSignatureForSelector:和forwardInvocation:方法来进行消息的转发处理,以规避此类问题。方法methodSignatureForSelector:返回一个任意一个非nil的NSMethodSignature对象,就可以进入到forwardInvocation:方法,在这个方法里可以转发消息,也可以什么都不做。 如下:

注意:实现forwardInvocation:方法时,不用调用super forwardInvocation:方法,否则,应用仍然会崩溃。 上述三种措施都可以有效的避免Unrecognized selector sent to instance xxx的崩溃发生,通常的做法是创建NSObject、UIViewController等基础类的子类ForwardNSObject、ForwardUIViewController等,实现消息转发处理,项目声明的所有其他类都继承ForwardNSObject、ForwardUIViewController类即可。

三、小结

以上内容即是萝莉给大家分享的全部内容,绝对是规避Unrecognized selector sent to instance xxx崩溃问题的利器,而实际上,崩溃的发生和规避的方式都是由Objective-C的Runtime特性决定的。所以,我们在以后的开发过程,除了要熟悉CocoaTouch框架,也要学习一些Runtime的内容,它一定会给你带来惊喜。 虽然我们知道了规避Unrecognized selector sent to instance xxx崩溃问题的方法,但对于开发者来说,不能规避后就不去关注这个崩溃问题 所以,对于开发者的建议是:实现ForwardInvocation后,通过宏定义控制在发布版本生效,在开发阶段的还是要把此类问题暴露,并尽早做修复处理。

如果你觉得内容意犹未尽,如果你想了解更多相关信息,请扫描以下二维码,关注我们的公众账号,可以获取更多技术类干货,还有精彩活动与你分享~

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!


用户评论
开源开发学习小组列表