iOS-Crash防护方案整理

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

你可能感兴趣的:(iOS-Crash防护方案整理)