1.UIKit Called on Non-Main Thread,非主线程刷新UI
通过runtime的方法替换,替换UIView 的 setNeedsLayout, layoutIfNeeded,layoutSubviews, setNeedsUpdateConstraints。方法,判断当前线程是否为主线程,如果不是,在主线程执行。
例如:
- (void)wt_safe_setNeedsLayout
{
if(![NSThread isMainThread]){
dispatch_async(dispatch_get_main_queue(), ^{
NSAssert(false, @"wt_safe_setNeedsLayout failed");
[self wt_safe_setNeedsLayout];
});
}else{
[self wt_safe_setNeedsLayout];
}
}
2.KVO
项目中KVO crash 占比很高, 主要原因为,添加删除不对称导致。
解决方法为,添加Map进行缓存。
所以我在addObserver:forKeyPath:options:context:这个方法里新建了一个字典,用来记录observer和keyPath。
下面需要解决的就是什么时候去调用的问题,我首先想到的是在dealloc时去判断是否添加了KVO的监视,但是当我直接交换了dealloc方法后发现,这个方法调用的太多了,并不适合直接交换,不然整个程序都会卡顿起来,需要找一个时机,于是我又修改为在添加监视的时候去交换dealloc,这次成功了,这样可以减少对不必要的类进行方法交换,同时提高效率。
唯一需要注意的是在ARC的情况下,不能直接@selector(dealloc)来做方法交换,需要变形一下NSSelectorFromString(@"dealloc"),这样才能做方法交换。
3.容器越界(NSArray, NSDictionary,…)
这个问题,就是运用方法替换进行,目前对以下类进行防范,类中可能导致crash 的方法,逐步进行增量扩充。
NSArray
NSMutableArray
NSAttributedString
NSDictionary
NSFileManager
NSIndexPath
NSJSONSerialization
NSMutableAttributedString
NSMutableDictionary
NSMutableSet
NSMutableString
NSSet
NSString
NSURL
4.nil值问题
方法替换,对于Nil不添加,不处理。
5.unrecognized selector crash (这个很多时候是由于class使用错误导致)
方法替换,对于unrecognized selector,执行一个空方法。
[NSObject jr_swizzleMethod:@selector(forwardingTargetForSelector:) withMethod:@selector(WT_safeForwardingTargetForSelector:) error:&error];
- (id)WT_safeForwardingTargetForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
if ([self respondsToSelector:aSelector] || signature) {
return [self WT_safeForwardingTargetForSelector:aSelector];
}
return [WTSafeGuard createFakeForwardTargetObject:self selector:aSelector];
}