雖然你們都不肯意看到程序崩潰,但可能崩潰是每一個應用必須面對的現實,既然崩潰已經發生,沒法阻擋了,那咱們就讓它崩也崩得淡定點吧。異步
iOS SDK中提供了一個現成的函數 NSSetUncaughtExceptionHandler 用來作異常處理,但功能很是有限,而引發崩潰的大多數緣由如:內存訪問錯誤,重複釋放等錯誤就無能爲力了。函數
由於這種錯誤它拋出的是Signal,因此必需要專門作Signal處理。oop
什麼是Signalspa
在計算機科學中,信號(英語:Signals)是Unix、類Unix以及其餘POSIX兼容的操做系統中進程間通信的一種有限制的方式。它是一種異步的通知機制,用來提醒進程一個事件已經發生。當一個信號發送給一個進程,操做系統中斷了進程正常的控制流程,此時,任何非原子操做都將被中斷。若是進程定義了信號的處理函數,那麼它將被執行,不然就執行默認的處理函數。操作系統
在項目工程中,要使用 Signal 時,經過引入 signal.h 來使用:代理
#include <sys/signal.h>
在 sys/signal 文件內定義了大量的系統信號標識code
使用這些信號標識,要經過函數 void (*signal(int, void (*)(int)))(int); 來進行使用,以下所示:orm
//定義一個接收到信號的回調函數 void HandleException(int signo) { printf("Lanou's sig is:%d",signo); } //註冊Alerm信號的回調函數 signal(SIGALRM, HandleException);
信號處理函數能夠經過 signal() 系統調用來設置。若是沒有爲一個信號設置對應的處理函數,就會使用默認的處理函數,不然信號就被進程截獲並調用相應的處理函數。在沒有處理函數的狀況下,程序能夠指定兩種行爲:忽略這個信號 SIG_IGN 或者用默認的處理函數 SIG_DFL 。可是有兩個信號是沒法被截獲並處理的: SIGKILL、SIGSTOP 。xml
建立一個SignalHandler靜態類對象
// // SignalHandler.h // RACSample // // Created by lewis on 4/29/15. // Copyright (c) 2015 lewis. All rights reserved. // #import <Foundation/Foundation.h> #include <sys/signal.h> @interface SignalHandler : NSObject //註冊捕獲信號的方法 + (void)RegisterSignalHandler; @end
在這個靜態類中,咱們引入了 sys/signal.h
而且添加了一個靜態方法 + (void)RegisterSignalHandler; 用來註冊信號通知消息.
SignalHandler.mm
// // SignalHandler.m // RACSample // // Created by lewis on 4/29/15. // Copyright (c) 2015 lewis. All rights reserved. // #import "SignalHandler.h" #import <UIKit/UIKit.h> #include <libkern/OSAtomic.h> #include <execinfo.h> //當前處理的異常個數 volatile int32_t UncaughtExceptionCount = 0; //最大可以處理的異常個數 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"]; //建立一個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]; } @implementation SignalHandler 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; } + (void)RegisterSignalHandler { //註冊程序因爲abort()函數調用發生的程序停止信號 signal(SIGABRT, HandleException); //註冊程序因爲非法指令產生的程序停止信號 signal(SIGILL, HandleException); //註冊程序因爲無效內存的引用致使的程序停止信號 signal(SIGSEGV, HandleException); //註冊程序因爲浮點數異常致使的程序停止信號 signal(SIGFPE, HandleException); //註冊程序因爲內存地址未對齊致使的程序停止信號 signal(SIGBUS, HandleException); //程序經過端口發送消息失敗致使的程序停止信號 signal(SIGPIPE, HandleException); } BOOL isDismissed = NO; //處理異經常使用到的方法 - (void)HandleException:(NSException *)exception { CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"程序出現問題啦" message:@"崩潰信息" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil]; [alertView show]; [alertView release],alertView = nil; //當接收到異常處理消息時,讓程序開始runloop,防止程序死亡 while (!isDismissed) { for (NSString *mode in (NSArray *)allModes) { CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); } } //當點擊彈出視圖的Cancel按鈕哦,isDimissed = YES,上邊的循環跳出 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按鈕,因此直接進行修改isDimsmissed這個變量了 isDismissed = YES; } @end
在.mm文件內,一共使用了以下幾個方法:
• HandleException 用來做爲signal函數的回調函數;
• Instance 用來獲取SignalHandler類的單例對象;
• RegisterSignalHandler 在 SignalHandler.h 頭文件中聲明的公開方法;
• - (void) HandleException:(NSException *)exption 用來做爲OC中處理異常的自定義方法
• - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex 用來做爲彈出視圖的代理對象
那麼經過這邊文章,你們應該能夠從容的處理iOS崩潰時的消息了。