iOS消息转发机制

2019-02-25  本文已影响0人  PageWen

使用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是指方法,之后的参数对应方法带的参数;

  1. 查找receiver对象的缓存方法,如果有缓存方法的话,则返回selector,没有缓存的话则去该类中搜索方法;
    2.1 首先,在本类的方法列表中查询;
    2.2 本类中没有查询到方法,则向上查询父类方法列表,以此向上,直到找到方法;
    2.3 把找到的方法缓存,并返回;
  2. 如果在本类及所有父类method_list中都没找到方法,则会调用resolveInstanceMethod,在这个方法中可以动态添加方法,但是不会做缓存;
  3. resolveInstanceMethod方法中没有动态添加方法,仍然有机会拦截异常抛出,就是在forwardingTargetForSelector方法中将方法转移到其他类去实现;
  4. 如果forwardingTargetForSelector中没有转发消息的话,系统会默认执行forwardInvocation方法,在执行forwardInvocation方法前要通过,methodSignatureForSelector方法获取方法签名,然后把方法签名,其中ObjCTypes类型有v(void)、@(对象)、:(方法)、l(长整型)、d(double类型)、i(int类型)等,详细的可以查看官方说明;
  5. 如果上面的转发方法都没有成功处理异常,系统则会调用doesNotRecognizeSelector方法

消息转发流程图

流程图.png
上一篇 下一篇

猜你喜欢

热点阅读