利用系統(tǒng)方法捕捉crash

首先我們要知道crash 的類型有哪些
1.數(shù)組越界導(dǎo)致的崩潰
2.數(shù)據(jù)集合類型,如字典、數(shù)組中插入元素時,插入空指針nil
3.調(diào)用當(dāng)前對象類中不存在的方法導(dǎo)致崩潰
4.調(diào)用的庫函數(shù)版本高于本機(jī)
5.內(nèi)存管理不當(dāng),向野指針發(fā)送消息導(dǎo)致的崩潰(包括某些信號量奔潰)
6.EXC_ARITHMETIC 除零操作
那么一般情況下,我們可以通過獲取信號量
崩潰的信息,來判斷崩潰的情況

信號量的崩潰

系統(tǒng)為我們提供了獲取信號量信息的方法
需要導(dǎo)入兩個頭文件

#include <libkern/OSAtomic.h>
#include <execinfo.h>

我們可以寫一個類方法來設(shè)置

+ (void)RegisterSignalHandler{
//注冊程序由于abort()函數(shù)調(diào)用發(fā)生的程序中止信號
    signal(SIGABRT, HandleException);
    
    //注冊程序由于非法指令產(chǎn)生的程序中止信號
    signal(SIGILL, HandleException);
    
    //注冊程序由于無效內(nèi)存的引用導(dǎo)致的程序中止信號
    signal(SIGSEGV, HandleException);
    
    //注冊程序由于浮點數(shù)異常導(dǎo)致的程序中止信號
    signal(SIGFPE, HandleException);
    
    //注冊程序由于內(nèi)存地址未對齊導(dǎo)致的程序中止信號
    signal(SIGBUS, HandleException);
    
    //程序通過端口發(fā)送消息失敗導(dǎo)致的程序中止信號
    signal(SIGPIPE, HandleException);
}

在每種對應(yīng)的信息下,我們可以寫一個方法去捕獲異常信息

//捕獲信號后的回調(diào)函數(shù)
//當(dāng)前處理的異常個數(shù)
volatile int32_t UncaughtExceptionCount = 0;
//最大能夠處理的異常個數(shù)
volatile int32_t UncaughtExceptionMaximum = 10;
void HandleException(int signo)
{
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum)
    {
        return;
    }
    NSMutableDictionary *userInfo =[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signo] forKey:@"signal"];
    
    //創(chuàng)建一個OC異常對象
    NSException *ex = [NSException exceptionWithName:@"SignalExceptionName" reason:[NSString stringWithFormat:@"Signal %d was raised.\n",signo] userInfo:userInfo];
    
    //處理異常消息
    [[SignalHandler Instance] performSelectorOnMainThread:@selector(HandleException:) withObject:ex waitUntilDone:YES];
}

那么在這個處理異常信息的時候我們用到的是單例來進(jìn)行管理的

static  SignalHandler *s_SignalHandler =  nil;

+ (instancetype)Instance{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (s_SignalHandler == nil) {
            s_SignalHandler  =  [[SignalHandler alloc] init];
        }
    });
    
    return s_SignalHandler;
}

接下來便是處理異常信息的方法,可以保存在本機(jī),可以上傳至服務(wù)端保存

BOOL isDismissed = NO;
//處理異常用到的方法
- (void)HandleException:(NSException *)exception
{
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
//創(chuàng)建沙盒保存路徑
    NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *errordir=[documentDirectory stringByAppendingPathComponent:@"errordump"];
    BOOL isdir;
    NSFileManager *fm=[NSFileManager defaultManager];
    if(![fm fileExistsAtPath:errordir isDirectory:&isdir]){
        [fm createDirectoryAtPath:errordir withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    NSString *str=[NSString stringWithFormat:@"%@:%@",exception,
                   [exception callStackSymbols]];
    //生成crash信息文本
    NSString *errorfile=[errordir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@signal_error.txt",[NSDate date]]];
    
    [str writeToFile:errorfile atomically:YES encoding:NSUTF8StringEncoding error:nil];
    
    NSLog(@"errorFile:%@",errorfile);
    //這里要獲取runloop
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
    
    //當(dāng)接收到異常處理消息時,讓程序開始runloop,防止程序死亡
    while (!isDismissed) {
        for (NSString *mode in (NSArray *)CFBridgingRelease(allModes))
        {
            CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
        }
    }
     //可以通過彈窗的方式告知用戶
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"程序出現(xiàn)問題啦" message:@"崩潰信息" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil];
    [alertView show];
    
    //當(dāng)點擊彈出視圖的Cancel按鈕,isDimissed = YES,上邊的循環(huán)跳出

    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)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex
{
    //因為這個彈出視圖只有一個Cancel按鈕,所以直接進(jìn)行修改isDimsmissed這個變量了
    isDismissed = YES;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容