要接入上海分部的自己的性能統(tǒng)計(jì)和事件統(tǒng)計(jì)的 sdk,在接入之前自己了解下。看到了一套捕獲異常的代碼。
對(duì)于異常分為了兩種,一種是系統(tǒng)拋出的 Exception 異常,iOS 提供了 NSSetUncaughtExceptionHandler 函數(shù)來進(jìn)行捕獲。對(duì)于另一種 Signal 異常需要自己單獨(dú)捕獲處理,最終還是轉(zhuǎn)成 Exception 異常上傳至服務(wù)器。
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--程序無效內(nèi)存中止信號(hào)
SIGBUS--程序內(nèi)存字節(jié)未對(duì)齊中止信號(hào)
SIGPIPE--程序Socket發(fā)送失敗中止信號(hào)
單獨(dú)創(chuàng)建一個(gè)異常捕獲的類 CaughtExceptionHandler。這個(gè)類在 appFinsh 方法中初始化。
+ (void)initHandler
{
???? NSSetUncaughtExceptionHandler(&HandleException);
???? signal(SIGABRT, SignalHandler);
????? signal(SIGILL, SignalHandler);
????? signal(SIGSEGV, SignalHandler);
????? signal(SIGFPE, SignalHandler);
?????? signal(SIGBUS, SignalHandler);
?????? signal(SIGPIPE, SignalHandler);
}
HandleException 函數(shù)是用來處理系統(tǒng)拋出的 Expcetion 異常。
void HandleException(NSException *exception)
{
? ? ? ? [[[CaughtExceptionHandler alloc] init]? performSelectorOnMainThread:@selector(handleException:) withObject:exception waitUntilDone:YES]; // 主線程執(zhí)行 handleException 方法
}
SignalHandler 捕獲系統(tǒng)發(fā)出的信號(hào)
void SignalHandler(int signal)
{
???? int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); // 32位并發(fā)的原子操作數(shù)
???? if (exceptionCount > UncaughtExceptionMaximum) {
????????? return;
???? }
??? NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
? ? NSArray *callStack = [NSThread callStackSymbols]; // 獲取調(diào)用函數(shù)棧
? [userInfo setObject:callStack forKey:CaughtExceptionHandlerAddressesKey];
??? NSException *exception = [NSException exceptionWithName:CaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %@ was raised.", nil), @(signal)] userInfo:userInfo]; // 將 Signal 信號(hào)轉(zhuǎn)成 NSException 異常傳出
? [[[CaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exception waitUntilDone:YES];
}
handleException 函數(shù)將 Exception 對(duì)象轉(zhuǎn)為字典,通過通知將其帶出。性能處理類接收該類通知通過通知對(duì)象獲取字典,將字典上傳服務(wù)器。
- (void)handleException:(NSException *)exception
{
????? NSTimeInterval crashTime = [[NSDate date] timeIntervalSince1970] * 1000; //毫秒級(jí)
????? NSString *name = [exception name]; //異常名稱
??? ? NSString *reason = [exception reason]; //異常原因
????? NSString *callStackSymbols = [[exception callStackSymbols] componentsJoinedByString:@"\n"];
????? if (callStackSymbols && callStackSymbols.length > 0) {
???????????? NSArray *callStack = [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey];
???????????? if (callStack.count > 0) {
????????????????? callStackSymbols = [callStack componentsJoinedByString:@"\n"];
???????????? }
????? }
????? NSInteger crashType = 1; //異常類型
????? NSInteger netType = 0; //網(wǎng)絡(luò)類型
????? NSInteger screenType = 1; //橫豎屏
?????? NSString *memoryInfo = [self currentMemoryInfo]; //剩余內(nèi)存
???? ? NSString *pageName = @""; //當(dāng)前頁面路徑
?????? NSMutableDictionary *crashDic = [NSMutableDictionary dictionary];
?????? [crashDic setObject:@(crashTime) forKey:ExceptionTime];
?????? [crashDic setObject:XT_NSSTRING_NOT_NIL(pageName) forKey:ExceptionPageName];
?????? [crashDic setObject:XT_NSSTRING_NOT_NIL(name) forKey:ExceptionName];
?????? [crashDic setObject:XT_NSSTRING_NOT_NIL(reason) forKey:ExceptionReason];
?????? [crashDic setObject:XT_NSSTRING_NOT_NIL(callStackSymbols) forKey:ExceptionStack];
??????? [crashDic setObject:@(crashType) forKey:ExceptionCrashType];
??????? [crashDic setObject:@(netType) forKey:ExceptionNetType];
??????? [crashDic setObject:@(screenType) forKey:ExceptionScreenType];
???????? [crashDic setObject:XT_NSSTRING_NOT_NIL(memoryInfo) forKey:ExceptionMemoryInfo];
???????? NSLog(@"crashCatcher has catch the crash %@, crash Reason is %@, crash Stack is %@", name, reason, callStackSymbols);
//發(fā)送通知
????????? NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:BFCrashCatcherCatchNotification object:nil userInfo:crashDic];
?????? CFRunLoopRef runLoop = CFRunLoopGetCurrent();
?????? CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
?????? while (!self.dismissed) {
????????????? for (NSString *mode in (__bridge NSArray *)allModes) {
??????????????????? CFRunLoopRunInMode((__bridge 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];
??????????? }
}