iOS开发无法避免程序Crash问题,开发过程中可以用调试技术捕获Crash进行修复,但是发布到App Store后,无法抓到这些问题,因此目前国内友盟、听云等公司抓到Crash后将Crash时的堆栈上传到他们服务器上,并可以通过dSYM文件找到Crash的方法接口。
当然我们自己也可以做到,首要问题是怎么抓到这些Crash原因和堆栈列表。iOS下面最常见的就是objective-c的NSException(通过@throw抛出,比如,NSArray访问元素越界、添加空对象等等)
-
Crash堆栈
设置方法
- 1.导入头文件
#include <signal.h>
#include <execinfo.h>
- 2.设置Crash捕获
- (void)initHandler {
struct sigaction newSignalAction;
memset(&newSignalAction, 0,sizeof(newSignalAction));
newSignalAction.sa_handler = &signalHandler;
sigaction(SIGABRT, &newSignalAction, NULL);
sigaction(SIGILL, &newSignalAction, NULL);
sigaction(SIGSEGV, &newSignalAction, NULL);
sigaction(SIGFPE, &newSignalAction, NULL);
sigaction(SIGBUS, &newSignalAction, NULL);
sigaction(SIGPIPE, &newSignalAction, NULL);
//异常时调用的函数
NSSetUncaughtExceptionHandler(&handleExceptions);
}
- 3.异常信息
void handleExceptions(NSException *exception) {
NSLog(@"exception = %@",exception);
NSLog(@"callStackSymbols = %@",[exception callStackSymbols]);
}
void signalHandler(int sig) {
//最好不要写,可能会打印太多内容
NSLog(@"signal = %d", sig);
}
- 4.将异常信息同步到自己服务器上
这样就可以抓到用户使用程序时的Crash了。
实例
- 1.在应用程序启动时初始化设置捕获,在以下方法中调用
initHandler
方法进行设置。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions```
- 2.写个异常程序验证,在某个控制器的```viewDidLoad```方法中写以下代码。
NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];
- 3.运行代码
2016-07-10 15:44:01.148 iOSCrash[37234:6862176] exception = *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
2016-07-10 15:44:01.149 iOSCrash[37234:6862176] callStackSymbols = (
0 CoreFoundation 0x000000010ca89d85 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010c4fddeb objc_exception_throw + 48
2 CoreFoundation 0x000000010c94acc5 -[__NSArrayM insertObject:atIndex:] + 901
3 iOSCrash 0x000000010bffb80f -[ViewController viewDidLoad] + 111
4 UIKit 0x000000010cfda984 -[UIViewController loadViewIfRequired] + 1198
5 UIKit 0x000000010cfdacd3 -[UIViewController view] + 27
6 UIKit 0x000000010ceb0fb4 -[UIWindow addRootViewControllerViewIfPossible] + 61
7 UIKit 0x000000010ceb169d -[UIWindow _setHidden:forced:] + 282
8 UIKit 0x000000010cec3180 -[UIWindow makeKeyAndVisible] + 42
9 UIKit 0x000000010ce37ed9 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
10 UIKit 0x000000010ce3e568 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1769
11 UIKit 0x000000010ce3b714 -[UIApplication workspaceDidEndTransaction:] + 188
12 FrontBoardServices 0x000000010f8a88c8 FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK + 24
13 FrontBoardServices 0x000000010f8a8741 -[FBSSerialQueue _performNext] + 178
14 FrontBoardServices 0x000000010f8a8aca -[FBSSerialQueue _performNextFromRunLoopSource] + 45
15 CoreFoundation 0x000000010c9af301 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 17
16 CoreFoundation 0x000000010c9a522c __CFRunLoopDoSources0 + 556
17 CoreFoundation 0x000000010c9a46e3 __CFRunLoopRun + 867
18 CoreFoundation 0x000000010c9a40f8 CFRunLoopRunSpecific + 488
19 UIKit 0x000000010ce3af21 -[UIApplication _run] + 402
20 UIKit 0x000000010ce3ff09 UIApplicationMain + 171
21 iOSCrash 0x000000010bffbb6f main + 111
22 libdyld.dylib 0x000000010f26392d start + 1
)
- 4.分析Crash
>4.1 从```exception```中得知Crash原因为Array中写了一个为nil的Object。
>4.2 从```callStackSymbols```堆栈符号中找到Crash的是第2条信息,堆栈列表可以理解为第0条为正在运行的,而该列表的第22是最早运行的,因此我们可以Crash的那条往下看可以知道这条Crash发生在```[ViewController viewDidLoad]```,即```ViewController```控制器的```viewDidLoad```方法中。
- 5.注意事项
>5.1 由于```NSSetUncaughtExceptionHandler```是```set```方法,所以只能设置一次,市面上很多抓Crash的库都会设置这个方法,因此,如果你用了UMeng Crash,Bugly(腾讯云内继承Bugly)等工具的话,最好不要自己设置,会影响Crash上报。
- **如果文章对你有帮助,请点下“喜欢”吧,感谢!**