RunLoop总结:RunLoop的应用场景(五)
今天要介紹的RunLoop應用場景感覺很酷炫,我們可能不常用到,但是對于做Crash 收集的 SDK可能會用得比較頻繁吧。相比關于RunLoop 可以讓應用起死回生,大家都聽說過,可是怎么實現呢?今天我就來實際試驗一下。
資料
- sunnyxx線下分享RunLoop (這是一份關于線下分享與討論RunLoop的視頻,備用地址:https://pan.baidu.com/s/1pLm4Vf9)
- 漫談iOS Crash收集框架(簡單介紹了下iOS 中Crash 的一些知識。)
- IOS程序異常crash捕獲與攔截 (我下面的Demo 就是在這部分代碼上做了簡化,以方便理解)
原理
iOS應用崩潰,常見的崩潰信息有EXC_BAD_ACCESS、SIGABRT XXXXXXX,而這里分為兩種情況,一種是未被捕獲的異常,我們只需要添加一個回調函數,并在應用啟動時調用一個 API即可;另一種是直接發送的 SIGABRT XXXXXXX,這里我們也需要監聽各種信號,然后添加回調函數。
針對情況一,其實我們都見過。我們在收集App崩潰信息時,需要添加一個函數 NSSetUncaughtExceptionHandler(&HandleException),參數 是一個回調函數,在回調函數里獲取到異常的原因,當前的堆棧信息等保存到 dump文件,然后供下次打開App時上傳到服務器。
其實,我們在HandleException回調函數中,可以獲取到當前的RunLoop,然后獲取該RunLoop中的所有Mode,手動運行一遍。
針對情況二,首先針對多種要捕獲的信號,設置好回調函數,然后也是在回調函數中獲取RunLoop,然后拿到所有的Mode,手動運行一遍。
代碼實現
第一步,我創建了一個處理類,并添加一個單例方法。(代碼見末尾的Demo)
第二步,在單例中對象實例化時,添加 異常捕獲 和 signal 處理的 回調函數。
- (void)setCatchExceptionHandler {// 1.捕獲一些異常導致的崩潰NSSetUncaughtExceptionHandler(&HandleException);// 2.捕獲非異常情況,通過signal傳遞出來的崩潰signal(SIGABRT, SignalHandler);signal(SIGILL, SignalHandler);signal(SIGSEGV, SignalHandler);signal(SIGFPE, SignalHandler);signal(SIGBUS, SignalHandler);signal(SIGPIPE, SignalHandler); }第三步,分別實現 異常捕獲的回調 和 signal 的回調。
void HandleException(NSException *exception) {// 獲取異常的堆棧信息NSArray *callStack = [exception callStackSymbols];NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];[userInfo setObject:callStack forKey:kCaughtExceptionStackInfoKey];CrashHandler *crashObject = [CrashHandler sharedInstance];NSException *customException = [NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo];[crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES]; }void SignalHandler(int signal) {// 這種情況的崩潰信息,就另某他法來捕獲吧NSArray *callStack = [CrashHandler backtrace];NSLog(@"信號捕獲崩潰,堆棧信息:%@",callStack);CrashHandler *crashObject = [CrashHandler sharedInstance];NSException *customException = [NSException exceptionWithName:kSignalExceptionNamereason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.", nil),signal]userInfo:@{kSignalKey:[NSNumber numberWithInt:signal]}];[crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES]; }第四步,添加讓應用起死回生的 RunLoop 代碼
- (void)handleException:(NSException *)exception {NSString *message = [NSString stringWithFormat:@"崩潰原因如下:\n%@\n%@",[exception reason],[[exception userInfo] objectForKey:kCaughtExceptionStackInfoKey]];NSLog(@"%@",message);UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"程序崩潰了"message:@"如果你能讓程序起死回生,那你的決定是?"delegate:selfcancelButtonTitle:@"崩就蹦吧"otherButtonTitles:@"起死回生", nil];[alert show];CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);while (!ignore) {for (NSString *mode in (__bridge NSArray *)allModes) {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:kSignalExceptionName]) {kill(getpid(), [[[exception userInfo] objectForKey:kSignalKey] intValue]);} else {[exception raise];} }因為我這里弄了一個AlertView彈窗,所以必須要回到主線程來處理。
實際上,RunLoop 相關的代碼:
完全可以寫在 上面的 HandleException 回調 和 SignalHandler回調中。
第五步,寫一段會導致崩潰的代碼
我是在ViewController 中添加了一個點擊事件,弄了一個數組越界的Bug:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {NSArray *array =[NSArray array];NSLog(@"%@",[array objectAtIndex:1]); }動態效果圖:
sunnyxx 稱之為回光返照,為什么呢?
我再一次點擊視圖,應用依然還是崩潰了,只能防止第一次崩潰。
我測試了,確實是第二次應用崩潰,未能起死回生。
文中的示例代碼都來自:RunLoopDemos中的RunLoopDemo04
轉載于:https://www.cnblogs.com/wanghang/p/6298800.html
總結
以上是生活随笔為你收集整理的RunLoop总结:RunLoop的应用场景(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 时间控件之赋值问题:datetimebo
- 下一篇: tp中使用分页技术