APP崩潰時(shí)做線程敝兰郑活彈出程序異常提示框

參考的文章是https://blog.csdn.net/u013602835/article/details/80485331

我們經(jīng)常會(huì)遇到APP閃退和崩潰的問(wèn)題测砂,那么我們應(yīng)該通過(guò)什么變量去監(jiān)聽APP的異常呢点弯?如何在程序崩潰時(shí)褐啡,保證程序不閃退,并給用戶彈出一個(gè)提示框呢? 這是本文將要講述的內(nèi)容停巷。

? ? 先介紹2個(gè)概念,Mach異常和Signal信號(hào)榕栏,如果想要監(jiān)聽異常其實(shí)就是去監(jiān)聽Mach異常和Signal信號(hào)畔勤。其實(shí)系統(tǒng)已經(jīng)給我們提供了一個(gè)方法去監(jiān)聽程序產(chǎn)生的異常,通過(guò)NSSetUncaughtExceptionHandler(入?yún)⑹且粋€(gè)C函數(shù))方法就可以直接捕獲異常信息扒磁。但是這個(gè)方法捕獲的異常有限庆揪,不能捕獲由于內(nèi)存訪問(wèn)錯(cuò)誤等產(chǎn)生的signal,所以如果想要監(jiān)聽絕大多數(shù)的異常需要我們自己通過(guò)注冊(cè)signal(signal的類型,回調(diào)方法)來(lái)捕獲信號(hào)進(jìn)行監(jiān)聽妨托。一缸榛、Mach異常

? ? Mach是Mac OS和iOS操作系統(tǒng)的微內(nèi)核核心检访,Mach異常是指最底層的內(nèi)核級(jí)異常 。每個(gè)thread仔掸,task都有一個(gè)異常端口數(shù)組脆贵,Mach的部分API暴露給了開發(fā)者,開發(fā)者可以直接通過(guò)Mach API設(shè)置thread起暮,task卖氨,host的異常端口,來(lái)監(jiān)聽捕獲Mach異常负懦,抓取Crash事件筒捺。所以當(dāng)APP中產(chǎn)生異常時(shí),最先能監(jiān)聽到異常的就是Mach纸厉。

二系吭、Signal信號(hào)

? ? 最先捕獲到異常的Mach在接下來(lái)會(huì)將所有的異常轉(zhuǎn)換為相應(yīng)的Unix信號(hào),并投遞到出錯(cuò)的線程颗品。之后就可以注冊(cè)想要監(jiān)聽的signal類型肯尺,來(lái)捕獲信號(hào)。如下躯枢,就是監(jiān)聽了SIGSEGV信號(hào)则吟,當(dāng)有SIGSEGV信號(hào)產(chǎn)生時(shí),就會(huì)回調(diào)mySignalHandler方法:

signal(SIGSEGV,mySignalHandler)

什么是Signal锄蹂?

? ? 在計(jì)算機(jī)科學(xué)中氓仲,信號(hào)(英語(yǔ):Signals)是Unix、類Unix以及其他POSIX兼容的操作系統(tǒng)中進(jìn)程間通訊的一種有限制的方式得糜。它是一種異步的通知機(jī)制敬扛,用來(lái)提醒進(jìn)程一個(gè)事件已經(jīng)發(fā)生。當(dāng)一個(gè)信號(hào)發(fā)送給一個(gè)進(jìn)程朝抖,操作系統(tǒng)中斷了進(jìn)程正常的控制流程啥箭,此時(shí),任何非原子操作都將被中斷槽棍。如果進(jìn)程定義了信號(hào)的處理函數(shù)捉蚤,那么它將被執(zhí)行抬驴,否則就執(zhí)行默認(rèn)的處理函數(shù)炼七。

? 如何使用Signal?

? ? ? 在項(xiàng)目工程中布持,要使用 Signal 時(shí)豌拙,通過(guò)引入 signal.h 來(lái)使用:

? ? ? #include <sys/signal.h>

? ? ? 在 sys/signal 文件內(nèi)定義了大量的系統(tǒng)信號(hào)標(biāo)識(shí)

使用這些信號(hào)標(biāo)識(shí),要通過(guò)函數(shù) void (*signal(int, void (*)(int)))(int); 來(lái)進(jìn)行使用,如下所示:


信號(hào)處理函數(shù)可以通過(guò) signal() 系統(tǒng)調(diào)用來(lái)設(shè)置题暖。如果沒有為一個(gè)信號(hào)設(shè)置對(duì)應(yīng)的處理函數(shù)按傅,就會(huì)使用默認(rèn)的處理函數(shù)捉超,否則信號(hào)就被進(jìn)程截獲并調(diào)用相應(yīng)的處理函數(shù)。在沒有處理函數(shù)的情況下唯绍,程序可以指定兩種行為:忽略這個(gè)信號(hào) SIG_IGN 或者用默認(rèn)的處理函數(shù) SIG_DFL 拼岳。但是有兩個(gè)信號(hào)是無(wú)法被截獲并處理的: SIGKILL、SIGSTOP 况芒。

Signal信號(hào)類型:

SIGABRT--程序中止命令中止信號(hào)

SIGALRM--程序超時(shí)信號(hào)

SIGFPE--程序浮點(diǎn)異常信號(hào)

SIGILL--程序非法指令信號(hào)

SIGHUP--程序終端中止信號(hào)

SIGINT--程序鍵盤中斷信號(hào)

SIGKILL--程序結(jié)束接收中止信號(hào)

SIGTERM--程序kill中止信號(hào)

SIGSTOP--程序鍵盤中止信號(hào)

SIGSEGV--程序無(wú)效內(nèi)存中止信號(hào)

SIGBUS--程序內(nèi)存字節(jié)未對(duì)齊中止信號(hào)

SIGPIPE--程序Socket發(fā)送失敗中止信號(hào)

三惜纸、當(dāng)APP崩潰時(shí)做線程保活绝骚,彈出程序異常提示框

UncaughtExceptionHandler里面是利用 iOS SDK中提供的現(xiàn)成函數(shù)NSSetUncaughtExceptionHandler 加上 注冊(cè)想要監(jiān)聽的signal類型耐版,來(lái)做異常處理的,通過(guò)拋出的Signal压汪,專門對(duì)Signal處理粪牲。?

void InstallUncaughtExceptionHandler(void) {

? ? NSSetUncaughtExceptionHandler(&HandleException); //系統(tǒng)的方法

? ? //添加想要監(jiān)聽的signal類型,當(dāng)發(fā)出相應(yīng)類型的signal時(shí)止剖,會(huì)回調(diào)SignalHandler方法

? ? signal(SIGABRT, SignalHandler);

? ? signal(SIGILL, SignalHandler);

? ? signal(SIGSEGV, SignalHandler);

? ? signal(SIGFPE, SignalHandler);

? ? signal(SIGBUS, SignalHandler);

? ? signal(SIGPIPE, SignalHandler);

}

void HandleException(NSException *exception)

{

? ? // 遞增的一個(gè)全局計(jì)數(shù)器腺阳,很快很安全,防止并發(fā)數(shù)太大

? ? int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

? ? if (exceptionCount > UncaughtExceptionMaximum) return;

? ? // 獲取 堆棧信息的數(shù)組

? ? NSArray *callStack = [UncaughtExceptionHandler backtrace];

? ? // 設(shè)置該字典

? ? NSMutableDictionary *userInfo =

? ? [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];

? ? // 給 堆棧信息 設(shè)置 地址 Key

? ? [userInfo

? ? setObject:callStack

? ? forKey:UncaughtExceptionHandlerAddressesKey];

? ? // 假如崩潰了執(zhí)行 handleException: 穿香,并且傳出 NSException

? ? [[[UncaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:[NSException exceptionWithName:[exception name] reason:[exception reason]

? ? ? userInfo:userInfo]

? ? waitUntilDone:YES];

}

void SignalHandler(int signal)

{

? ? // 遞增的一個(gè)全局計(jì)數(shù)器舌狗,很快很安全,防止并發(fā)數(shù)太大

? ? int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

? ? if (exceptionCount > UncaughtExceptionMaximum) return;

? ? // 設(shè)置是哪一種 single 引起的問(wèn)題

? ? NSMutableDictionary *userInfo =

? ? [NSMutableDictionary

? ? dictionaryWithObject:[NSNumber numberWithInt:signal]

? ? forKey:UncaughtExceptionHandlerSignalKey];

? ? // 獲取堆棧信息數(shù)組

? ? NSArray *callStack = [UncaughtExceptionHandler backtrace];

? ? // 寫入地址

? ? [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];

? ? //? 假如崩潰了執(zhí)行 handleException: 扔水,并且傳出 NSException

? ? [[[UncaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? withObject: [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason: [NSString stringWithFormat:

? ? ? NSLocalizedString(@"Signal %d was raised.", nil),signal]


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]] waitUntilDone:YES];

}

此處注意OSAtomicIncrement32的使用痛侍,它此處是一個(gè)遞增的一個(gè)全局計(jì)數(shù)器,效果又快又安全魔市,是為了防止并發(fā)數(shù)太大出現(xiàn)錯(cuò)誤的情況主届。

+ (NSArray *)backtrace

{

? ? void* callstack[128];

? ? //? 該函數(shù)用來(lái)獲取當(dāng)前線程調(diào)用堆棧的信息,獲取的信息將會(huì)被存放在buffer中(callstack),它是一個(gè)指針數(shù)組。

? ? int frames = backtrace(callstack, 128);

? ? //? backtrace_symbols將從backtrace函數(shù)獲取的信息轉(zhuǎn)化為一個(gè)字符串?dāng)?shù)組.

? ? char **strs = backtrace_symbols(callstack, frames);

? ? NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

? ? for (

? ? ? ? int i = UncaughtExceptionHandlerSkipAddressCount;

? ? ? ? i < UncaughtExceptionHandlerSkipAddressCount + UncaughtExceptionHandlerReportAddressCount;

? ? ? ? i++)

? ? {

? ? ? ? [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];

? ? }

? ? free(strs); // 記得free

? ? return backtrace;

}

這邊值得注意的是下面這兩個(gè)函數(shù)方法

int backtrace(void**,int) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

char** backtrace_symbols(void* const*,int) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

該函數(shù)用來(lái)獲取當(dāng)前線程調(diào)用堆棧的信息待德,并且轉(zhuǎn)化為字符串?dāng)?shù)組君丁。

最后再來(lái)處理,此處涉及到 CFRunLoopRunInMode将宪, kill值得注意绘闷!

- (void)handleException:(NSException *)exception

{

? ? // 打印或彈出框

? ? // TODO :


? ? // 接到程序崩潰時(shí)的信號(hào)進(jìn)行自主處理

? ? CFRunLoopRef runLoop = CFRunLoopGetCurrent();

? ? CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

? ? while (!dismissed)

? ? {

? ? ? ? //循環(huán)切換mode

? ? ? ? for (NSString *mode in (NSArray *)allModes) {

? ? ? ? ? ? CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);

? ? ? ? }

? ? }

? ? CFRelease(allModes);

? ? // 下面等同于清空之前設(shè)置的

? ? 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];

? ? }

}

? ? 當(dāng)異常產(chǎn)生時(shí),上邊代碼就可以捕獲Crash異常较坛,然后彈出一個(gè)程序異常提示框印蔗。

? ? 注意:NSSetUncaughtExceptionHandler方法在純Swift工程中不好使,無(wú)法監(jiān)聽到異常丑勤。

原文鏈接:https://blog.csdn.net/u013602835/article/details/80485331

總結(jié):如果想要監(jiān)聽異常其實(shí)就是去監(jiān)聽Mach異常和Signal信號(hào)华嘹。其實(shí)系統(tǒng)已經(jīng)給我們提供了一個(gè)方法去監(jiān)聽程序產(chǎn)生的異常,通過(guò)NSSetUncaughtExceptionHandler(入?yún)⑹且粋€(gè)C函數(shù))方法就可以直接捕獲異常信息法竞。但是這個(gè)方法捕獲的異常有限耙厚,不能捕獲由于內(nèi)存訪問(wèn)錯(cuò)誤等產(chǎn)生的signal强挫,所以如果想要監(jiān)聽絕大多數(shù)的異常需要我們自己通過(guò)注冊(cè)signal(signal的類型,回調(diào)方法)來(lái)捕獲信號(hào)進(jìn)行監(jiān)聽。

利用 iOS SDK中提供的現(xiàn)成函數(shù)NSSetUncaughtExceptionHandler 加上 注冊(cè)想要監(jiān)聽的signal類型薛躬,來(lái)做異常處理的俯渤,通過(guò)拋出的Signal,專門對(duì)Signal處理型宝。?

NSSetUncaughtExceptionHandler方法在純Swift工程中不好使稠诲,無(wú)法監(jiān)聽到異常。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诡曙,一起剝皮案震驚了整個(gè)濱河市臀叙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌价卤,老刑警劉巖劝萤,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異慎璧,居然都是意外死亡床嫌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門胸私,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厌处,“玉大人,你說(shuō)我怎么就攤上這事岁疼±妫” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵捷绒,是天一觀的道長(zhǎng)瑰排。 經(jīng)常有香客問(wèn)我,道長(zhǎng)暖侨,這世上最難降的妖魔是什么椭住? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮字逗,結(jié)果婚禮上京郑,老公的妹妹穿的比我還像新娘。我一直安慰自己葫掉,他們只是感情好些举,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挖息,像睡著了一般金拒。 火紅的嫁衣襯著肌膚如雪兽肤。 梳的紋絲不亂的頭發(fā)上套腹,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天绪抛,我揣著相機(jī)與錄音,去河邊找鬼电禀。 笑死幢码,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尖飞。 我是一名探鬼主播症副,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼政基!你這毒婦竟也來(lái)了贞铣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沮明,失蹤者是張志新(化名)和其女友劉穎辕坝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荐健,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酱畅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了江场。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纺酸。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖址否,靈堂內(nèi)的尸體忽然破棺而出餐蔬,到底是詐尸還是另有隱情,我是刑警寧澤佑附,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布用含,位于F島的核電站,受9級(jí)特大地震影響帮匾,放射性物質(zhì)發(fā)生泄漏啄骇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一瘟斜、第九天 我趴在偏房一處隱蔽的房頂上張望缸夹。 院中可真熱鬧,春花似錦螺句、人聲如沸虽惭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芽唇。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匆笤,已是汗流浹背研侣。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炮捧,地道東北人庶诡。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像咆课,于是被迫代替她去往敵國(guó)和親末誓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 簡(jiǎn)介: 利用NSSetUncaughtExceptionHandler可以用來(lái)處理異常崩潰书蚪。崩潰報(bào)告系統(tǒng)會(huì)用NSS...
    飛哥漂流記閱讀 5,671評(píng)論 0 8
  • 最近項(xiàng)目上需要對(duì)崩潰信息進(jìn)行處理喇澡,要滿足崩潰后及時(shí)捕捉到崩潰信息,當(dāng)應(yīng)用下次打開后再將報(bào)文上傳至服務(wù)器...
    迷失之刃閱讀 4,674評(píng)論 9 8
  • UncaughtExceptionHandler 業(yè)務(wù)場(chǎng)景描述 收集APP崩潰信息殊校,上傳到服務(wù)器撩幽,用于分析統(tǒng)計(jì). ...
    SherwinChen閱讀 845評(píng)論 0 0
  • 要接入上海分部的自己的性能統(tǒng)計(jì)和事件統(tǒng)計(jì)的 sdk,在接入之前自己了解下箩艺〈茏恚看到了一套捕獲異常的代碼。 對(duì)于異常分為...
    南京小伙閱讀 858評(píng)論 2 1
  • 首先介紹一款工具 定位解析信號(hào)異常上github上搜索DSYMTools,和郵件發(fā)送SKPSMTPMessage ...
    coderK閱讀 709評(píng)論 0 1