iOS - Crash防护主要有以下几个方面
1,子线程访问UI:通过runtime拦截几个UI方法,并且监听当前线程,如果不是主线程则强行转回主线程,并且可以进行上报
[objc_getClass("UIView") swizzleMethod:@selector(setNeedsLayout) swizzledSelector:@selector(replace_setNeedsLayout)];
[objc_getClass("UIView") swizzleMethod:@selector(setNeedsDisplay) swizzledSelector:@selector(replace_setNeedsDisplay)];
-(void)replace_setNeedsLayout{
if ([NSThread isMainThread]) {
return [self replace_setNeedsLayout];
}else{
dispatch_async(dispatch_get_main_queue(), ^{
return [self replace_setNeedsLayout];
});
}
}
-(void)replace_setNeedsDisplay{
if ([NSThread isMainThread]) {
return [self replace_setNeedsDisplay];
}else{
dispatch_async(dispatch_get_main_queue(), ^{
return [self replace_setNeedsDisplay];
});
}
}
2,调用不存在函数(hook:forwardingTargetForSelector,把方法转到一个自定义对象,并在改对象添加该不存在方法
[objc_getClass("NSObject") swizzleMethod:@selector(forwardingTargetForSelector:) swizzledSelector:@selector(replace_forwardingTargetForSelector:)];
- (id)replace_forwardingTargetForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
if ([self respondsToSelector:aSelector] || signature) {
return [self replace_forwardingTargetForSelector:aSelector];
}
return [NSObject createFakeForwardTargetObject:self selector:aSelector];
}
+ (id)createFakeForwardTargetObject:(id)aTarget selector:(SEL)aSelector
{
if ([[NSString string] respondsToSelector:aSelector]) {
NSString *szTarget = nil;
if ([aTarget isKindOfClass:[NSNumber class]]) {
szTarget = [NSString stringWithFormat:@"%@", aTarget];
}
if (szTarget) {
return szTarget;
}
}
FakeForwardTargetObject *fakeTaget = [[FakeForwardTargetObject alloc] initWithSelector:aSelector];
return fakeTaget;
}
3,KVO重复添加监听回调去重功能,KVO重复remove导致crash问题.方案:runtime拦截,添加移除Api,通过KVC拿到私有api,进行判断去重
[objc_getClass("NSObject") swizzleMethod:@selector(removeObserver:forKeyPath:) swizzledSelector:@selector(removeDasen:forKeyPath:)];
[objc_getClass("NSObject") swizzleMethod:@selector(addObserver:forKeyPath:options:context:) swizzledSelector:@selector(addDasen:forKeyPath:options:context:)];
// 交换后的方法
- (void)removeDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
if ([self observerKeyPath:keyPath observer:observer]) {
[self removeDasen:observer forKeyPath:keyPath];
}
}
// 交换后的方法
- (void)addDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
objc_setAssociatedObject(self, "addObserverFlag", @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (![self observerKeyPath:keyPath observer:observer]) {
[self addDasen:observer forKeyPath:keyPath options:options context:context];
}
}
// 进行检索获取Key
- (BOOL)observerKeyPath:(NSString *)key observer:(id )observer
{
id info = self.observationInfo;
NSArray *array = [info valueForKey:@"_observances"];
for (id objc in array) {
id Properties = [objc valueForKeyPath:@"_property"];
id newObserver = [objc valueForKeyPath:@"_observer"];
NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
return YES;
}
}
return NO;
}
4,iOS9.0系统以上监听对象dealloc,没有移除通知导致崩溃问题.方案:通过runtimehook添加和deallocApi,进行移除操作
[objc_getClass("NSNotificationCenter") swizzleMethod:@selector(addObserver:selector:name:object:) swizzledSelector:@selector(ghl_addObserver:selector:name:object:)];
[objc_getClass("NSObject") swizzleMethod:NSSelectorFromString(@"dealloc") swizzledSelector:@selector(replaceDealloc)];
- (void)replaceDealloc
{
NSString *addObserver = objc_getAssociatedObject(self, "addObserverFlag");
if ([addObserver boolValue]) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
[self replaceDealloc];
}
- (void)ghl_addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject {
[self ghl_addObserver:observer selector:aSelector name:aName object:anObject];
objc_setAssociatedObject(observer, "addObserverFlag", @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
5,容器类问题:oc数组越界,字符串操作越界,UITableView和UICollectionView操作数据越界问题.具体查看demo.这里可以提一句,类似NSArray这种类是一种类簇,类似一种多态的操作,内部根据某些情况会调用具体的类
demo:https://github.com/riceForChina/SafeTool.git
参考文章:https://neyoufan.github.io/2017/01/13/ios/BayMax_HTSafetyGuard/#kvo