- 问题:
在方法调用中,对某个对象发送或者调用了无法被识别的消息。 - 思路:
通过OC
在运行时的消息转发机制拦截方法的调用
1.调用resolveInstanceMethod
给个机会让类添加这个实现这个函数。
2.调用forwardingTargetForSelector
让别的对象去执行这个函数。
3.调用forwardInvocation
(函数执行器)灵活的将目标函数以其他形式执行。 - 解决方案:
通过在forwardingTargetForSelector
中把消息转发给一个专门用来实现消息转发的空对象,把目标方法的IMP
指针指向一个返回值为0的空函数。
- 问题:
1.KVO
的被观察者已经被dealloc
,然而KVO
的观察者还未被释放。
2.KVO
重复添加观察者或重复移除观察者(KVO
注册观察者与移除观察者不匹配)导致的 crash。 - 思路:
正常逻辑下,添加和删除Observer
走的是两个固定的方法,从这两个方法下手。 - 解决方案:
swizzle
三个方法:addObserver
,removeObserver
,dealloc
。 通过一个中间层--ObserverCenter
对所有的添加移除操作进行统计分析、对重复添加和重复删除的行为进行检测并处理。
- 问题:
接受通知的对象已经被释放,发送通知的对象还未被释放,导致消息发送失败。这个问题在ios9.0
上已经被系统和谐的处理。下面着重是面向ios8.0~ios9.0
系统的处理。 - 思路:
在dealloc
方法中移除所有的通知。 - 解决方案:
swizzle
对象的dealloc
方法,移除所有通知。
- 问题:
由于NSTimer
强引用于target
、NSTimer
被runloop
强引用,如果没有在合适的时机去invalidate
这个NSTimer
的话,这个target
就永远无法被释放。 - 思路:
找个三方的timerTarget
,代替当前的target
。从而使NSTimer
无法干预到target
的释放。只需要在 target 释放的时候invalidate
这个NSTimer
和释放三方timerTarget
。 - 解决方案:
swizzle
NSTimer
的scheduledTimerWithTimeInterval
、timerWithTimeInterval
、initWithFireDate
等等相关的方法。替换其中的target
为timerTarget
,并将target
作为timerTarget
的代理(弱引用)实现timer
中的消息转发。(对于使用block
的初始化方法请生活自理)
- 问题:
NSArray/NSDictionary/NSString
等等插入空值或者取值越界都会导致程序Crash
。 - 思路:
监测容器类的取值赋值操作,并进行监管。 - 解决方案:
在NSArray/NSDictionary/NSString
等容器类中swizzle
它们的赋值操作取值操作,对不健康不正常的操作采取异常收集上报与和谐处理的操作。
- 问题:
因为UI
操作涉及到屏幕的展示必须在主线程下实现,所以对于一些不小心在异步执行的UI操作会导致程序的crash
。 - 思路:
监听替换更新UI
前可能会触发调用的函数方法。 - 解决方案:
监听下方法的调用,判断是否在主线程中,如果不在则放入主线程中调用。
- (void)setNeedsLayout;
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)rect;
- 问题:
因为上述的方法无法包罗所有的UI更新操作,有遗漏的各种UI操作需要逐个统计添加。
- 问题:
访问野指针导致的Crash
占了很大一部分,野指针类型crash的表现为:Exception Type:SIGSEGV
,Exception Codes: SEGV_ACCERR
- 思路:
可以尝试通过一个用来替换野指针的Zombie
对象来防止野指针访问的Crash
。(会对内存有一些损耗,所以需要提前预估好可以接受的内存损耗预估值,做好内存管理工作) - 解决方案:
1.swizzling
替换NSObject
的allocWithZone
方法,在新的方法中判断该类型对象是否需要加入野指针防护,如果需要,则通过objc_setAssociatedObject
为该对象设置flag
标记,被标记的对象后续会进入zombie
流程。
2.swizzling
替换NSObject
的dealloc
方法,对flag
标记的对象实例调用objc_destructInstance
,释放该实例引用的相关属性,然后将实例的isa
修改为ZombieObject
。通过objc_setAssociatedObject
保存将原始类名保存在该实例中。
3.在ZombieObject
通过消息转发机制forwardingTargetForSelector
处理所有拦截的方法,根据selector
动态添加能够处理方法的响应者StubObject
实例,然后通过objc_getAssociatedObject
获取之前保存该实例对应的原始类名,统计错误数据。
4.使用LRU
对ZombieObject
进行内存管理,内存消耗达到顶值的时候进行相关对象释放工作。