App崩溃前上传数据或者操作UI iOS

iOS应用崩溃,常见的崩溃信息有 EXC_BAD_ACCESS 、 SIGABRT XXXXXXX ,而这里分为两种情况,一种是未被捕获的异常,我们只需要添加一个回调函数,并在应用启动时调用一个 API即可; 另

一种是直接发送的 SIGABRT XXXXXXX ,这里我们也需要监听各种信号,然后添加回调函数。

针对情况一,其实我们都见过。 我们在收集App崩溃信息时,需要添加一个函数 NSSetUncaughtExceptionHandler(&HandleException) ,参数 是一个回调函数,在回调函数里获取到异常的

原因,当前的堆栈信息等保存到 dump文件,然后供下次打开App时上传到服务器。

其实,我们在HandleException回调函数中,可以获取到当前的RunLoop,然后获取该RunLoop中的所有Mode,手动运行一遍。

针对情况二,首先针对多种要捕获的信号,设置好回调函数,然后也是在回调函数中获取RunLoop,然后拿到所有的Mode,手动运行一遍。

 

代码实现

第一步,我创建了一个处理类 ZSExceptionHandler ,并添加一个单例方法。

+ (instancetype)shareInstance
{
    static ZSExceptionHandler *handler = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        handler = [[self alloc] init];
    });
    return handler;
}

 

第二步,在单例中对象实例化时,添加 异常捕获 和 signal 处理的 回调函数。

- (void)startCatchExceptionHandler
{
    // 1.捕获一些异常导致的崩溃
    NSSetUncaughtExceptionHandler(&HandleException);
    // 2.捕获非异常情况,通过signal传递出来的崩溃
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
}

 

第三步,分别实现 异常捕获的回调 和 signal 的回调。里面可以都封装成异常模型 NSException

void HandleException(NSException *exception)
{
    ZSExceptionHandler *obj = ZSExceptionHandler.shareInstance;
    [obj handleException];
}



void SignalHandler(int signal)
{
    ZSExceptionHandler *obj = ZSExceptionHandler.shareInstance;

    [obj handleException];
}

 

第四步,在崩溃前上传数据或者操作UI

-(void) handleException
{

    //如果有操作UI的地方 需要放到主线程
    @synchronized (self) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"程序崩溃了" message:@"如果你能让程序起死回生,那你的决定是?" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"崩就蹦吧" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
        }];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"起死回生" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            
        }];
        [alertController addAction:sureAction];
        [alertController addAction:cancelAction];
        
        [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
    }
    

    //每种mode需要延迟3s去上传数据到后台
    BOOL need = YES;
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
    if (need) {
        for (NSString *mode in (__bridge NSArray *)allModes) {
            CFRunLoopRunInMode((CFStringRef)mode, 3, 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);

}

 

第五步,写一段会导致崩溃的代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSArray *array =[NSArray array];
    NSLog(@"%@",[array objectAtIndex:1]);
}

 

第六步,App启动时,开启崩溃捕获

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [ZSExceptionHandler.shareInstance startCatchExceptionHandler];
    return YES;
}

 

App崩溃前上传数据或者操作UI iOS

上一篇:go语言游戏服务端开发(一)——架构


下一篇:(转)探索C++的秘密之详解extern "C",这就是为什么很多.lib被我们正确调用确总是无法解析的。