美文网首页iOS常用
iOS 崩溃处理机制(持续更新)

iOS 崩溃处理机制(持续更新)

作者: 光之盐汽水 | 来源:发表于2020-09-28 18:59 被阅读0次

    应用程序的崩溃总是最让人头疼的问题,也是非常严重的研发事故,那么应该如果降低程序的崩溃率呢?这里就用到了“APP运行时Crash自动修复+捕获系统”。

    思路:
    利用Objective-C语言的动态特性,采用AOP(Aspect Oriented Programming) 面向切面编程的设计思想,做到无痕植入。能够自动在app运行时实时捕获导致app崩溃的破环因子,然后通过特定的技术手段去化解这些破坏因子,使app免于崩溃,照样可以继续正常运行,为app的持续运转保驾护航。

    ps:我们不可能强大到把所有类型的crash都处理掉,但是我们会对一些高频的crash进行处理,从而降低crash率。

    我们常见的crash有哪些呢?
    1、unrecognized selector crash (没找到对应的函数)
    2、KVO crash :(KVO的被观察者dealloc时仍然注册着KVO导致的crash,添加KVO重复添加观察者或重复移除观察者 )
    3、NSNotification crash:(当一个对象添加了notification之后,如果dealloc的时候,仍然持有notification)
    4、NSTimer类型crash:(需要在合适的时机invalidate 定时器,否则就会由于定时器timer强引用target的关系导致 target不能被释放,造成内存泄露,甚至在定时任务触发时导致crash)
    5、Container类型crash:(数组,字典,常见的越界,插入,nil)
    6、野指针类型的crash
    7、非主线程刷UI类型:(在非主线程刷UI将会导致app运行crash)

    一、unrecognized selector crash

    unrecognized selector类型的crash,通常是因为一个对象调用了一个不属于它方法的方法导致的。而我们可以从方法调用的过程中,寻找到避免程序崩溃的突破口。

    方法调用的过程是哪样的呢?

    方法调用的过程--调用实例方法
    1.在对象的<缓存方法列表> 中去找要调用的方法,找到直接执行其实现。
    2.对象的<缓存方法列表> 里没找到,就去<类的方法列表>里找,找到了就执行其实现。
    3.还没找到,说明这个类自己没有了,就会通过isa去向其父类里执行1、2。
    4.如果找到了根类还没找到,那么就是没有了,会转向一个拦截调用的方法,可以自己在拦截调用方法里面做一些处理。
    5.如果没有在拦截调用里做处理,那么就会报错崩溃。
    
    方法调用的过程--调用类方法
    1.在类的<缓存方法列表> 中去找要调用的方法,找到直接执行其实现。
    2.类的<缓存方法列表> 里没找到,就去<meta类的方法列表>里找,找到了就执行其实现。
    3.还没找到,说明这个类自己没有了,就会通过isa去meta类的父类里执行1、2。
    4.如果找到了根meta类还没找到,那么就是没有了,会转向一个拦截调用的方法,可以自己在拦截调用方法里面做一些处理。
    5.如果没有在拦截调用里做处理,那么就会报错崩溃。
    

    从上面的方法调用过程可以看出,在找不到调用的方法程序崩溃之前,我们可以通过重写NSObject方法进行拦截调用,阻止程序的crash。这里面就用到了消息的转发机制:


    消息的转发机制

    由上图我们不难发现,runtime提供了3种方式去补救:
    1:调用resolveInstanceMethod给个机会让类添加这个实现这个函数
    2:调用forwardingTargetForSelector让别的对象去执行这个函数
    3:调用forwardInvocation(函数执行器)灵活的将目标函数以及其他形式执行。
    如果都不行,系统才会调用doesNotRecognizeSelector抛出异常。

    既然可以补救,我们完全也可以利用消息转发机制来做文章,但是我们选择哪一步比较合适呢?
    1:resolveInstanceMethod需要在类的本身动态的添加它本身不存在的方法,这些方法对于该类本身来说是冗余的
    2:forwardInvocation可以通过NSInvocation的形式将消息转发给多个对象,但是其开销比较大,需要创建新的 NSInvocation对象,并且forwardInvocation的函数经常被使用者调用来做消息的转发选择机制,不适合多次重写
    3:forwardingTargetForSelector可以将消息转发给一个对象,开销较小,并且被重写的概率较低,适合重写

    对于NSObject方法的重写,我们可以分为以下几步:
    第一步:为类动态的创建一个消息接受类。
    第二步:为类动态为桩类添加对应的Selector,用一个通用的返回0的函数来实现该SEL的IMP
    第三步:将消息直接转发到这个消息接受类类对象上。

    具体实现,查看:iOS崩溃处理机制:Unrecognized Selector


    二、KVO Crash

    KVO Crash,通常是KVO的被观察者dealloc时仍然注册着KVO导致的crash,添加KVO重复添加观察者或重复移除观察者引起的。
    一个被观察的对象上有若干个观察者,每个观察者又有若干条keypath。如果观察者和keypathx的数量一多,很容易不清楚被观察的对象整个KVO关系,导致被观察者在dealloc的时候,仍然残存着一些关系没有被注销,同时还会导致KVO注册者和移除观察者不匹配的情况发生。尤其是多线程的情况下,导致KVO重复添加观察者或者移除观察者的情况,这种类似的情况通常发生的比较隐蔽,很难从代码的层面上排查。

    解决方法:

    可以让观察对象持有一个KVO的delegate,所有和KVO相关的操作均通过delegate来进行管理,delegate通过建立一张MAP表来维护KVO的整个关系,这样做的好处有2个:
    1:如果出现KVO重复添加观察或者移除观察者(KVO注册者不匹配的)情况,delegate,可以直接阻止这些非正常的操作。
    2:被观察对象dealloc之前,可以通过delegate自动将与自己有关的KVO关系都注销掉,避免了KVO的被观察者dealloc时仍然注册着KVO导致的crash

    具体实现,查看:iOS崩溃处理机制:KVO Crash


    三、NSNotification Crash

    产生的原因:
    当一个对象添加了notification之后,如果dealloc的时候,仍然持有notification,就会出现NSNotification类型的crash。NSNotification类型的crash多产生于程序员写代码时候犯疏忽,在NSNotificationCenter添加一个对象为observer之后,忘记了在对象dealloc的时候移除它。

    iOS9之前会crash,iOS9之后苹果系统已优化。在iOS9之后,即使开发者没有移除observer,Notification crash也不会再产生了。

    解决方案:
    NSNotification Crash的防护原理很简单, 利用method swizzling hook NSObject的dealloc函数,再对象真正dealloc之前先调用一下:[[NSNotificationCenter defaultCenter] removeObserver:self],即可。

    具体实现,查看:iOS崩溃处理机制:NSNotification Crash


    四、NSTimer Crash 防护

    产生的原因:
    NSTimer会 强引用 target实例,所以需要在合适的时机invalidate 定时器,否则就会由于定时器timer强引用target的关系导致 target不能被释放,造成内存泄露,甚至在定时任务触发时导致crash。与此同时,如果NSTimer是无限重复的执行一个任务的话,也有可能导致target的selector一直被重复调用且处于无效状态,对app的CPU,内存等性能方面均是没有必要的浪费。所以,很有必要设计出一种方案,可以有效的防护NSTimer的滥用问题。

    解决方案:
    定义一个抽象类,NSTimer实例强引用抽象类,而在抽象类中,弱引用target,这样target和NSTimer之间的关系也就是弱引用了,意味着target可以自由的释放,从而解决了循环引用的问题。

    具体实现,查看:iOS崩溃处理机制:NSTimer Crash防护


    五、Container类型crash防护

    Container类型的crash 指的是容器类的crash,常见的有NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache的crash。 一些常见的越界,插入nil,等错误操作均会导致此类crash发生。

    解决方案:
    对于容易造成crash的方法,自定义方法进行交换,并在自定义的方法中加入一些条件限制和判断。

    具体实现,查看:iOS崩溃处理机制:Container类型crash防护


    (未完待续)

    相关文章

      网友评论

        本文标题:iOS 崩溃处理机制(持续更新)

        本文链接:https://www.haomeiwen.com/subject/vrcjuktx.html