iOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHandler 用来做异常处理,但功能非常有限,而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。
1.函数实现
void UncaughtExceptionHandler(NSException *exception) {
NSString *reason = [exception reason];//非常重要,就是崩溃的原因
NSString *name = [exception name];//异常类型
NSLog(@"崩溃日志: exception type : %@ \n crash reason : %@ \n", name, reason);
}
2.函数调用
- (void)viewDidLoad {
[super viewDidLoad];
// 捕获异常
NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
}
3.其他属性(自己进入xcode查找)
@property (readonly, copy) NSExceptionName name;
@property (nullable, readonly, copy) NSString *reason;
@property (nullable, readonly, copy) NSDictionary *userInfo;
@property (readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses NS_AVAILABLE(10_5, 2_0);
@property (readonly, copy) NSArray<NSString *> *callStackSymbols NS_AVAILABLE(10_6, 4_0);
4.实际应用
在iOS中获取崩溃信息的方式有很多,比较常见的是使用友盟,bugly等第三方分析工具,或者自己收集崩溃信息并上传公司服务器。
详细教程
void uncaughtExceptionHandler(NSException *exception)
{
// 异常的堆栈信息
NSArray *stackArray = [exception callStackSymbols];
// 出现异常的原因
NSString *reason = [exception reason];
// 异常名称
NSString *name = [exception name];
NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];
NSLog(@"%@", exceptionInfo);
NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];
[tmpArr insertObject:reason atIndex:0];
//保存到本地 -- 当然你可以在下次启动的时候,上传这个log
[exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()] atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
return YES;
}
5.crash符号化(每次编译,.dSYM文件都会变化,所以必须一一对应)
1.找到symbolicatecrash的目录
find /Applications/Xcode.app -name symbolicatecrash -type f
2.将symbolicatecrash 和 xxx.crash以及AppName.app.dSYM放在同一个文件夹下面
3.打开终端,用命令切换到 crash 文件夹下,运行下面命令
./symbolicatecrash xxx.crash AppName.app.dSYM > result.crash
6.xcode运行测试和Jenkins
- 1 .xcode调试的的时候,如果出现崩溃,打开日志会自动解析
- 2.Jenkins编译后,直接打包下载编译后的文件,里解压后里面有对应的xxx.dSYM,然后从导出crash文件,按照第5步,进行操作.
7.注意:以下情况不会有崩溃信息产生
1.内存访问错误(不是野指针错误)
2.低内存,当程序内存使用过多会造成系统低内存的问题,系统会将程序内存回收
3.因为某种原因触发看门狗机制
8.第三方工具恶意覆盖
image崩溃收集统计函数应该只进行一次调用,如果用第三方的话也最好只用一个第三方,这样我们获取崩溃统计信息的途径也是唯一的。
第三方统计工具并不是用的越多越好,使用多个崩溃收集第三方会导致NSSetUncaughtExceptionHandler()函数指针的恶意覆盖,导致有些第三方不能收到崩溃信息。
现在很多第三方崩溃收集工具为了确保自己能最大可能的收集到崩溃信息,会对NSSetUncaughtExceptionHandler()函数指针的恶意覆盖。因为这个函数是将函数地址当做参数传递,所以只要重复调用就会被覆盖,这样就不能保证崩溃收集的稳定性。
我们解析崩溃信息时,看到崩溃堆栈只有main.m文件中的崩溃,并且可以确定不是因为main.m文件中的bug导致的崩溃,就基本可以确定是NSSetUncaughtExceptionHandler()函数指针被恶意覆盖。
网友评论