美文网首页ios 进阶
iOS-Crash防护方案整理

iOS-Crash防护方案整理

作者: 不拘小节123456 | 来源:发表于2021-09-28 17:40 被阅读0次

    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防护方案整理

        本文链接:https://www.haomeiwen.com/subject/xxsrnltx.html