使用oc开发的开发者们,或多或少的接触过类似的崩溃unrecognized selector sent to instance 0x60000001b200'
,出现这种问题的原因很好理解,就是未实现对象的方法。但是在实际开发中要如何取避免这种崩溃的产生呢?这就需要用到今天的主题--消息转发。
通过崩溃日志的栈信息
0 CoreFoundation 0x00000001065b91e6 __exceptionPreprocess + 294
1 libobjc.A.dylib 0x00000001033d2031 objc_exception_throw + 48
2 CoreFoundation 0x000000010663a784 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x000000010653b898 ___forwarding___ + 1432
4 CoreFoundation 0x000000010653b278 _CF_forwarding_prep_0 + 120
5 PQMsgForwardDemo 0x0000000102ace689 -[ViewController viewDidLoad] + 121
我们可以看出在抛出异常前,编译器有去调用一些方法,如forwarding_prep_0
,-[NSObject(NSObject) doesNotRecognizeSelector:]
等方法,所以我们利用aop的思路,在这些方法中做处理,从而避免崩溃的产生。
iOS方法调用在runtime中调用过程
oc调用方法一般为[obj foo]
,这样便表示对象调用了一个方法,但是这么一个简单的语句在runtime中却有着丰富的操作。
1.[obj foo]
会转换成obj_msgSend(id receiver,cmd sel,arg1,arg2,... )
,其中receiver为发起对象obj,sel是指方法,之后的参数对应方法带的参数;
- 查找receiver对象的缓存方法,如果有缓存方法的话,则返回selector,没有缓存的话则去该类中搜索方法;
2.1 首先,在本类的方法列表中查询;
2.2 本类中没有查询到方法,则向上查询父类方法列表,以此向上,直到找到方法;
2.3 把找到的方法缓存,并返回; - 如果在本类及所有父类method_list中都没找到方法,则会调用
resolveInstanceMethod
,在这个方法中可以动态添加方法,但是不会做缓存; - 在
resolveInstanceMethod
方法中没有动态添加方法,仍然有机会拦截异常抛出,就是在forwardingTargetForSelector
方法中将方法转移到其他类去实现; - 如果
forwardingTargetForSelector
中没有转发消息的话,系统会默认执行forwardInvocation
方法,在执行forwardInvocation
方法前要通过,methodSignatureForSelector
方法获取方法签名,然后把方法签名,其中ObjCTypes
类型有v(void)、@(对象)、:(方法)、l(长整型)、d(double类型)、i(int类型)等,详细的可以查看官方说明; - 如果上面的转发方法都没有成功处理异常,系统则会调用
doesNotRecognizeSelector
方法
消息转发流程图
![](https://img.haomeiwen.com/i10509618/2791797aaadf2da7.png)
网友评论