要接入上海分部的自己的性能统计和事件统计的 sdk,在接入之前自己了解下。看到了一套捕获异常的代码。
对于异常分为了两种,一种是系统抛出的 Exception 异常,iOS 提供了 NSSetUncaughtExceptionHandler 函数来进行捕获。对于另一种 Signal 异常需要自己单独捕获处理,最终还是转成 Exception 异常上传至服务器。
Signal 信号类型:
SIGABRT--程序中止命令中止信号
SIGALRM--程序超时信号
SIGFPE--程序浮点异常信号
SIGILL--程序非法指令信号
SIGHUP--程序终端中止信号
SIGINT--程序键盘中断信号
SIGKILL--程序结束接收中止信号
SIGTERM--程序kill中止信号
SIGSTOP--程序键盘中止信号
SIGSEGV--程序无效内存中止信号
SIGBUS--程序内存字节未对齐中止信号
SIGPIPE--程序Socket发送失败中止信号
单独创建一个异常捕获的类 CaughtExceptionHandler。这个类在 appFinsh 方法中初始化。
+ (void)initHandler
{
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
}
HandleException 函数是用来处理系统抛出的 Expcetion 异常。
void HandleException(NSException *exception)
{
[[[CaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exception waitUntilDone:YES]; // 主线程执行 handleException 方法
}
SignalHandler 捕获系统发出的信号
void SignalHandler(int signal)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); // 32位并发的原子操作数
if (exceptionCount > UncaughtExceptionMaximum) {
return;
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
NSArray *callStack = [NSThread callStackSymbols]; // 获取调用函数栈
[userInfo setObject:callStack forKey:CaughtExceptionHandlerAddressesKey];
NSException *exception = [NSException exceptionWithName:CaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %@ was raised.", nil), @(signal)] userInfo:userInfo]; // 将 Signal 信号转成 NSException 异常传出
[[[CaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exception waitUntilDone:YES];
}
handleException 函数将 Exception 对象转为字典,通过通知将其带出。性能处理类接收该类通知通过通知对象获取字典,将字典上传服务器。
- (void)handleException:(NSException *)exception
{
NSTimeInterval crashTime = [[NSDate date] timeIntervalSince1970] * 1000; //毫秒级
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; //网络类型
NSInteger screenType = 1; //横竖屏
NSString *memoryInfo = [self currentMemoryInfo]; //剩余内存
NSString *pageName = @""; //当前页面路径
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);
//发送通知
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];
}
}