Skip to content

Latest commit

 

History

History
73 lines (65 loc) · 5.18 KB

兜底文档.MD

File metadata and controls

73 lines (65 loc) · 5.18 KB

Crash防护方案分析

1.Unrecognized Selector类型Crash防护

  • 问题:
    在方法调用中,对某个对象发送或者调用了无法被识别的消息。
  • 思路:
    通过OC在运行时的消息转发机制拦截方法的调用
    1.调用 resolveInstanceMethod 给个机会让类添加这个实现这个函数。
    2.调用 forwardingTargetForSelector 让别的对象去执行这个函数。
    3.调用 forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
  • 解决方案:
    通过在 forwardingTargetForSelector 中把消息转发给一个专门用来实现消息转发的空对象,把目标方法的IMP指针指向一个返回值为0的空函数。

2.KVO类型crash防护

  • 问题:
    1.KVO 的被观察者已经被 dealloc,然而 KVO 的观察者还未被释放。
    2.KVO重复添加观察者或重复移除观察者( KVO 注册观察者与移除观察者不匹配)导致的 crash。
  • 思路:
    正常逻辑下,添加和删除 Observer 走的是两个固定的方法,从这两个方法下手。
  • 解决方案:
    swizzle 三个方法:addObserver, removeObserver, dealloc。 通过一个中间层--ObserverCenter 对所有的添加移除操作进行统计分析、对重复添加和重复删除的行为进行检测并处理。

3.NSNotification类型crash防护

  • 问题:
    接受通知的对象已经被释放,发送通知的对象还未被释放,导致消息发送失败。这个问题在ios9.0上已经被系统和谐的处理。下面着重是面向ios8.0~ios9.0系统的处理。
  • 思路:
    dealloc 方法中移除所有的通知。
  • 解决方案:
    swizzle 对象的 dealloc 方法,移除所有通知。

4.NSTimer类型的内存无法释放Crash防护

  • 问题:
    由于 NSTimer 强引用于 targetNSTimerrunloop 强引用,如果没有在合适的时机去 invalidate 这个 NSTimer 的话,这个 target 就永远无法被释放。
  • 思路:
    找个三方的 timerTarget ,代替当前的 target。从而使 NSTimer 无法干预到 target 的释放。只需要在 target 释放的时候 invalidate 这个 NSTimer 和释放三方 timerTarget
  • 解决方案:
    swizzle NSTimerscheduledTimerWithTimeIntervaltimerWithTimeIntervalinitWithFireDate 等等相关的方法。替换其中的 targettimerTarget,并将 target 作为 timerTarget 的代理(弱引用)实现 timer 中的消息转发。(对于使用 block 的初始化方法请生活自理)

5.越界&赋值问题的crash防护(其中包含KVC的问题)

  • 问题:
    NSArray/NSDictionary/NSString 等等插入空值或者取值越界都会导致程序 Crash
  • 思路:
    监测容器类的取值赋值操作,并进行监管。
  • 解决方案:
    NSArray/NSDictionary/NSString 等容器类中 swizzle 它们的赋值操作取值操作,对不健康不正常的操作采取异常收集上报与和谐处理的操作。

6.UI操作不在主线程导致的Crash防护

  • 问题:
    因为 UI 操作涉及到屏幕的展示必须在主线程下实现,所以对于一些不小心在异步执行的UI操作会导致程序的 crash
  • 思路:
    监听替换更新 UI 前可能会触发调用的函数方法。
  • 解决方案:
    监听下方法的调用,判断是否在主线程中,如果不在则放入主线程中调用。
    - (void)setNeedsLayout;
    - (void)setNeedsDisplay;
    - (void)setNeedsDisplayInRect:(CGRect)rect;
  • 问题:
    因为上述的方法无法包罗所有的UI更新操作,有遗漏的各种UI操作需要逐个统计添加。

7.野指针被访问导致的Crash防护

  • 问题:
    访问野指针导致的 Crash 占了很大一部分,野指针类型crash的表现为:Exception Type:SIGSEGVException Codes: SEGV_ACCERR
  • 思路:
    可以尝试通过一个用来替换野指针的 Zombie 对象来防止野指针访问的 Crash。(会对内存有一些损耗,所以需要提前预估好可以接受的内存损耗预估值,做好内存管理工作)
  • 解决方案:
    1.swizzling 替换 NSObjectallocWithZone 方法,在新的方法中判断该类型对象是否需要加入野指针防护,如果需要,则通过 objc_setAssociatedObject 为该对象设置 flag 标记,被标记的对象后续会进入 zombie 流程。
    2.swizzling 替换 NSObjectdealloc 方法,对 flag 标记的对象实例调用 objc_destructInstance,释放该实例引用的相关属性,然后将实例的 isa 修改为 ZombieObject。通过 objc_setAssociatedObject 保存将原始类名保存在该实例中。
    3.在 ZombieObject 通过消息转发机制 forwardingTargetForSelector 处理所有拦截的方法,根据 selector 动态添加能够处理方法的响应者 StubObject 实例,然后通过 objc_getAssociatedObject 获取之前保存该实例对应的原始类名,统计错误数据。
    4.使用LRUZombieObject进行内存管理,内存消耗达到顶值的时候进行相关对象释放工作。