IOS程序crash捕获

来源:互联网 发布:nginx 自定义变量 编辑:程序博客网 时间:2024/04/29 16:52

IOS程序crash捕获

基础理论

Crash分为两种,一种是程序抛出的异常,没有被捕获造成的;另一种是signal类型的异常。针对未被捕获的异常可以使用NSSetUncaughtExceptionHandler系统方法来设置异常处理函数;对于signal类型的异常,需要使用signal系统方法给每种需要处理的signal类型的异常设置处理函数。如果没有为一个信号设置对应的处理函数,就会使用默认的处理函数SIG_DFL。

Signal的常见类型:

SIGABRT——程序中止信号

SIGFPE——浮点异常信号

SIGILL——非法指令异常

SIGSEGV——无效内存异常

SIGBUS——内存字节未对齐异常

SIGPIPE——socket发送失败异常

 

头文件”execinfo.h”声明了几个函数用于获取当前线程的函数调用堆栈,在程序出错的时候,打印出调用堆栈是非常有用的。

int backtrace(void **buffer,int size)

该函数用于获取当前线程的调用堆栈,获取的信息将被存储在buffer中,它是一个指针列表。参数size用来指定buffer中可以保存多少个指针元素。函数返回值是实际获取的指针个数,最大不超过size的大小。

 

char ** backtrace_symbols (void *const *buffer, int size)

backtrace_symbols将从backtrace函数中获取的信息转化为字符串数组。参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace的返回值)。函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址

 

解决方案

这里我定义一个类UncaughtExceptionHandler处理以上提及的两种异常类型。大致思路:分别给两种类型的异常注册处理函数,然后以弹出视图的方式显示异常信息。

头文件UncaughtExceptionHandler.h

#import <Foundation/Foundation.h>@interface UncaughtExceptionHandler : NSObject{    BOOL dismissed;//是否继续程序}@end//处理未捕获的异常void HandleUncaughtException(NSException *exception);//处理信号类型的异常void HandleSignal(int signal);//为两种类型的信号注册处理函数void InstallUncaughtExceptionHandler(void);

实现文件UncaughtExceptionHandler.m
<pre name="code" class="objc">////  UncaughtExceptionHandler.m//  crashTest////  Created by HuberySun on 15/11/11.//  Copyright © 2015年 HuberySun. All rights reserved.//#import "UncaughtExceptionHandler.h"#import <UIKit/UIKit.h>#include <libkern/OSAtomic.h>#include <execinfo.h>NSString * const UncaughtExceptionHandlerSignalExceptionName=@"UncaughtExceptionHandlerSignalExceptionName";NSString * const UncaughtExceptionHandlerSignalKey=@"UncaughtExceptionHandlerSignalKey";NSString * const UncaughtExceptionHandlerAddressesKey=@"UncaughtExceptionHandlerAddressesKey";volatile int32_t exceptionCount = 0;const int32_t exceptionMaximum = 10;const NSInteger UncaughtExceptionHandlerReportAddressCount = 10;//指明报告多少条调用堆栈信息@interface UncaughtExceptionHandler ()<UIAlertViewDelegate>//获取堆栈指针,返回符号化之后的数组+ (NSArray *)backtrace;//处理异常2,包括抛出的异常和信号异常- (void)handleException:(NSException *)exception;@end@implementation UncaughtExceptionHandler+ (NSArray *)backtrace{    void *callStack[128];//堆栈方法数组    int frames=backtrace(callStack, 128);//从iOS的方法backtrace中获取错误堆栈方法指针数组,返回数目    char **strs=backtrace_symbols(callStack, frames);//符号化        int i;    NSMutableArray *symbolsBackTrace=[NSMutableArray arrayWithCapacity:frames];    for (i=0; i<UncaughtExceptionHandlerReportAddressCount; i++) {        [symbolsBackTrace addObject:[NSString stringWithUTF8String:strs[i]]];    }    free(strs);    return symbolsBackTrace;}- (void)handleException:(NSException *)exception{    NSString *message=[NSString stringWithFormat:@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开\n\n异常报告:\n异常名称:%@\n异常原因:%@\n其他信息:%@\n",                       [exception name],                       [exception reason],                       [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]];    UIAlertView *alert =    [[UIAlertView alloc]      initWithTitle:NSLocalizedString(@"抱歉,程序出现了异常", nil)      message:message      delegate:self      cancelButtonTitle:@"退出"      otherButtonTitles:@"继续", nil];    [alert show];        ///////////////    CFRunLoopRef runLoop=CFRunLoopGetCurrent();    CFArrayRef allModes=CFRunLoopCopyAllModes(runLoop);    NSArray *arr=(__bridge NSArray *)allModes;    while (!dismissed) {        for (NSString *mode in arr) {            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:UncaughtExceptionHandlerSignalExceptionName])    {        kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);    }    else    {        [exception raise];    }}- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{    if (buttonIndex==0) {        dismissed=YES;    }else{        dismissed=false;    }}@endvoid HandleUncaughtException(NSException *exception){    int32_t exceptionCount=OSAtomicIncrement32(&exceptionCount);    if (exceptionCount>exceptionMaximum) {        return;    }        NSArray *callStack=[UncaughtExceptionHandler backtrace];    NSMutableDictionary *userInfo=[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];    [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];        UncaughtExceptionHandler *uncaughtExceptionHandler=[[UncaughtExceptionHandler alloc] init];    NSException *uncaughtException=[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo];    [uncaughtExceptionHandler performSelectorOnMainThread:@selector(handleException:) withObject:uncaughtException waitUntilDone:YES];}void HandleSignal(int signal){    int32_t exceptionCount= OSAtomicIncrement32(&exceptionCount);    if (exceptionCount>exceptionMaximum) {        return;    }        NSMutableDictionary *userInfo=[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];    NSArray *callBack=[UncaughtExceptionHandler backtrace];    [userInfo setObject:callBack forKey:UncaughtExceptionHandlerAddressesKey];        UncaughtExceptionHandler *uncaughtExceptionHandler=[[UncaughtExceptionHandler alloc] init];    NSException *signalException=[NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:@"Signal %d was raised.",signal] userInfo:userInfo];    [uncaughtExceptionHandler performSelectorOnMainThread:@selector(handleException:) withObject:signalException waitUntilDone:YES];}void InstallUncaughtExceptionHandler(void){    NSSetUncaughtExceptionHandler(HandleUncaughtException);//设置未捕获的异常处理        //设置信号类型的异常处理    signal(SIGABRT, HandleSignal);    signal(SIGILL, HandleSignal);    signal(SIGSEGV, HandleSignal);    signal(SIGFPE, HandleSignal);    signal(SIGBUS, HandleSignal);    signal(SIGPIPE, HandleSignal);}


测试

测试代码
- (IBAction)OccurCrash:(id)sender {    NSArray *arr=[NSArray arrayWithObjects:@"4",@"5", nil];    NSLog(@"%@",[arr objectAtIndex:3]);}

点击按钮,访问含有两个元素的数组的第三个索引位置的元素,会触发异常,弹出提示窗口,如下


商业应用解决方案

对于发布到app store的应用,大多使用第三方的Crash统计工具,比如Crashlytics


参考信息

[iOS]使用signal让app能够从容崩溃

IOS程序异常crash捕获与拦截

Linux下利用backtrace追踪函数调用堆栈以及定位段错误


0 0