iOS Signal异常拦截

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

常见的Crash分为ExceptionSignal两种
常规的Exception

image-22.png

  • 当触发Exception异常,可以使用上述方法成功拦截

Signal异常的产生

image-23.png

  • 当触发Signal异常,单凭NSSetUncaughtExceptionHandler注册回调函数是无法拦截到的,我们需要针对Signal进行额外的处理

Crash分析中的Signalwww.jianshu.com/p/3a9dc6bd5…

Signal的回调函数

LGUncaughtExceptionHandler类中,增加对Signal的处理

+ (void)installUncaughtExceptionHandler {

    NSSetUncaughtExceptionHandler(&LGExceptionHandlers);
    //针对Signal的处理
    signal(SIGABRT, LGSignalHandler);
    signal(SIGILL, LGSignalHandler);
    signal(SIGSEGV, LGSignalHandler);
    signal(SIGFPE, LGSignalHandler);
    signal(SIGBUS, LGSignalHandler);
    signal(SIGPIPE, LGSignalHandler);
}
复制代码

处理Signal异常

进入LGSignalHandler函数

//处理signal报错
void LGSignalHandler(int signal) {
    
    int32_t exceptionCount = OSAtomicIncrement32(&LGUncaughtExceptionCount);
    // 如果太多不用处理
    if (exceptionCount > LGUncaughtExceptionCount) {
        return;
    }
    
    NSString* description = nil;
    switch (signal) {
        case SIGABRT:
            description = [NSString stringWithFormat:@"Signal SIGABRT was raised!\n"];
            break;
        case SIGILL:
            description = [NSString stringWithFormat:@"Signal SIGILL was raised!\n"];
            break;
        case SIGSEGV:
            description = [NSString stringWithFormat:@"Signal SIGSEGV was raised!\n"];
            break;
        case SIGFPE:
            description = [NSString stringWithFormat:@"Signal SIGFPE was raised!\n"];
            break;
        case SIGBUS:
            description = [NSString stringWithFormat:@"Signal SIGBUS was raised!\n"];
            break;
        case SIGPIPE:
            description = [NSString stringWithFormat:@"Signal SIGPIPE was raised!\n"];
            break;
        default:
            description = [NSString stringWithFormat:@"Signal %d was raised!",signal];
    }
    
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    NSArray *callStack = [LGUncaughtExceptionHandler backtrace];
    [userInfo setObject:callStack forKey:LGUncaughtExceptionHandlerAddressesKey];
    [userInfo setObject:[NSNumber numberWithInt:signal] forKey:LGUncaughtExceptionHandlerSignalKey];
    
    NSException *ex = [NSException exceptionWithName:LGUncaughtExceptionHandlerSignalExceptionName reason:description userInfo:userInfo];
    
    //在主线程中,执行指定的方法, withObject是执行方法传入的参数
    [[[LGUncaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(lg_handleException:) withObject:ex waitUntilDone:YES];
}
复制代码
  • signal一起包装到NSMutableDictionary
  • 创建一个自定义名称和描述的NSException
  • 调用LGUncaughtExceptionHandler类的lg_handleException对象方法

进入lg_handleException方法,包含ExceptionSigna的处理

- (void)lg_handleException:(NSException *)exception{
    NSLog(@"%@", exception);

    UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Crash" message:nil preferredStyle:UIAlertControllerStyleAlert];
    [controller addAction:[UIAlertAction actionWithTitle:@"继续执行" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

    }]];
    
    [controller addAction:[UIAlertAction actionWithTitle:@"退出程序" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        self.dismissed = YES;
    }]];
    
    UIViewController *rootController = [UIApplication sharedApplication].keyWindow.rootViewController;
    [rootController presentViewController:controller animated:true completion:nil];
    
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

    while (!self.dismissed) {
        //点击继续
        for (NSString *mode in (__bridge NSArray *)allModes) {
            //快速切换Mode
            CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
        }
    }

    //点击退出
    CFRelease(allModes);
    NSSetUncaughtExceptionHandler(NULL);
    
    signal(SIGABRT, SIG_DFL);
    signal(SIGILL, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    signal(SIGFPE, SIG_DFL);
    signal(SIGBUS, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);

    if ([[exception name] isEqual:LGUncaughtExceptionHandlerSignalExceptionName]) {
        kill(getpid(), [[[exception userInfo] objectForKey:LGUncaughtExceptionHandlerSignalKey] intValue]);
    } else {
        [exception raise];
    }
}
复制代码
  • Signa的监听回收相应内存
  • 对自定义NSException进行特殊处理

猜你喜欢

转载自juejin.im/post/7085716261073584142