iOS-iOS异常捕获和崩溃日志
来源:互联网 发布:淘宝对3c认证的处罚 编辑:程序博客网 时间:2024/06/16 02:40
1.前言
开发iOS应用,解决Crash问题始终是一个难题。Crash分为两种,一种是由EXC_BAD_ACCESS引起的,原因是访问了不属于本进程的内存地址,有可能是访问已被释放的内存;另一种是未被捕获的Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。其实对于未捕获的Objective-C异常,我们是有办法将它记录下来的,如果日志记录得当,能够解决绝大部分崩溃的问题。这里对于UI线程与后台线程分别说明。
网上的捕获异常的方法都大同小异,都是处理处理signal信号。这里我把捕获的异常保存到缓存目录上,然后你可以通过邮件或者网络接口把自己发给自己或者公司,便于自己分析和在下个版本上进行修复工作。
2.创建单利
单利主要做有一下功能,捕获崩溃日志、存取日子和界面提示功能;详见ZFJUncaughtExceptionHandler.h的方法;
#import <Foundation/Foundation.h>//返回地址路径typedef void(^ logPathBlock)(NSString *pathStr);@interface ZFJUncaughtExceptionHandler : NSObject+ (instancetype)shareInstance;@property (nonatomic,copy) logPathBlock pathBlock;//是否显示错误提示框 默认是不显示的@property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^showAlert)(BOOL yesOrNo);//是否显示错误信息@property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^showErrorInfor)(BOOL yesOrNo);//回调返回错误日志@property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^getlogPathBlock)(void(^ logPathBlock)(NSString *pathStr));//错误日志路径@property (nonatomic,strong) NSString *logFilePath;ZFJUncaughtExceptionHandler * InstanceZFJUncaughtExceptionHandler(void);@end
初始化+ (instancetype)shareInstance{ static ZFJUncaughtExceptionHandler *_manager = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _manager = [[self alloc] init]; [_manager uiConfig]; }); return _manager;}#pragma mark - 设置日志存取的路径- (void)uiConfig{ NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *filePath = [docPath stringByAppendingPathComponent:@"ZFJUncaughtExceptionHandlerLog.txt"]; NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:filePath]) { [fileManager createFileAtPath:filePath contents:[@"~~~~~~~~~~~~~~~~~~程序异常日志~~~~~~~~~~~~~~~~~~\n\n" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; } self.logFilePath = filePath;}
默认我是打开异常捕获的,当然你可以这这里面添加一个字段来控制打开或者关闭捕获功能;ZFJUncaughtExceptionHandler *InstanceZFJUncaughtExceptionHandler(void){ NSSetUncaughtExceptionHandler(&HandleException); signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler); return [ZFJUncaughtExceptionHandler shareInstance];}
可以通过添加一个字段install来打开或者关闭,例如:NSSetUncaughtExceptionHandler(install ? HandleException : NULL); signal(SIGABRT, install ? SignalHandler : SIG_DFL); signal(SIGILL, install ? SignalHandler : SIG_DFL); signal(SIGSEGV, install ? SignalHandler : SIG_DFL); signal(SIGFPE, install ? SignalHandler : SIG_DFL); signal(SIGBUS, install ? SignalHandler : SIG_DFL); signal(SIGPIPE, install ? SignalHandler : SIG_DFL);
从异常堆栈里面获取异常信息;void HandleException(NSException *exception){ int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); //如果太多不用处理 if (exceptionCount > UncaughtExceptionMaximum) { return; } //获取调用堆栈 NSArray *callStack = [exception callStackSymbols]; NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]]; [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey]; //在主线程中,执行制定的方法, withObject是执行方法传入的参数 [[ZFJUncaughtExceptionHandler shareInstance] performSelectorOnMainThread:@selector(handleException:) withObject:[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo] waitUntilDone:YES];}void SignalHandler (int signal){ int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); if (exceptionCount > UncaughtExceptionMaximum) { return; } NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]; NSArray *callStack = [ZFJUncaughtExceptionHandler backtrace]; [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey]; [[ZFJUncaughtExceptionHandler shareInstance] performSelectorOnMainThread:@selector(handleException:) withObject: [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason: [NSString stringWithFormat: NSLocalizedString(@"Signal %d was raised.", nil), signal] userInfo: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]] waitUntilDone:YES];}
处理异常- (void)handleException:(NSException *)exception{ //保存日志 可以发送日志到自己的服务器上 [self validateAndSaveCriticalApplicationData:exception]; NSString *_erroeMeg = nil; NSString *userInfo = [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]; if(self.isShowErrorInfor){ _erroeMeg = [NSString stringWithFormat:NSLocalizedString(@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开\n" @"异常原因如下:\n%@\n%@", nil), [exception reason], userInfo]; }else{ _erroeMeg = [NSString stringWithFormat:NSLocalizedString(@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开", nil)]; } UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"抱歉,程序出现了异常" message:_erroeMeg delegate:self cancelButtonTitle:@"退出" otherButtonTitles:@"继续", nil]; dispatch_async(dispatch_get_main_queue(), ^{ if(_isShowAlert){ [alert show]; } }); CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); while (!_dismissed){ for (NSString *mode in (__bridge NSArray *)allModes) { CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); } } CFRelease(allModes);#pragma clang diagnostic pop 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]; }}
异常日志保存到本地;#pragma mark - 保存错误信息日志- (void)validateAndSaveCriticalApplicationData:(NSException *)exception{ NSString *exceptionMessage = [NSString stringWithFormat:NSLocalizedString(@"\n******************** %@ 异常原因如下: ********************\n%@\n%@\n==================== End ====================\n\n", nil), [self currentTimeString], [exception reason], [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]; NSFileHandle *handle = [NSFileHandle fileHandleForUpdatingAtPath:self.logFilePath]; [handle seekToEndOfFile]; [handle writeData:[exceptionMessage dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; if(self.pathBlock){ self.pathBlock(self.logFilePath); }}
3.函数调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. //简单调用 //InstanceZFJUncaughtExceptionHandler(); //链式调用 是否显示警告框 是否显示错误信息 是否回调日志地址 InstanceZFJUncaughtExceptionHandler().showAlert(YES).showErrorInfor(YES).getlogPathBlock(^(NSString *logPathStr){ NSLog(@"程序异常日志地址 == %@",logPathStr); }); //当然我们也可以直接直接获取日志文件地址 //NSString *logFilePath = InstanceZFJUncaughtExceptionHandler().logFilePath; return YES;}
4.运行效果
5.DEMO下载
点击下载
http://download.csdn.net/detail/u014220518/9705941
0 0
- iOS-iOS异常捕获和崩溃日志
- 捕获异常崩溃日志(iOS)
- iOS crash捕获异常崩溃日志
- iOS 捕获程序崩溃日志
- iOS崩溃 捕获异常处理
- iOS崩溃 捕获异常处理
- iOS崩溃 捕获异常处理
- iOS崩溃 捕获异常处理
- Android和iOS开发中异常崩溃的捕获
- iOS 中捕获程序崩溃日志
- iOS 中捕获程序崩溃日志
- iOS 中捕获程序崩溃日志
- iOS 中捕获程序崩溃日志
- iOS开发 中捕获程序崩溃日志
- iOS中捕获用户的崩溃日志
- iOS 中捕获程序崩溃日志
- iOS 中捕获程序崩溃日志
- iOS-中捕获程序崩溃日志-NSSetUncaughtExceptionHandler
- weblogic与tomcat对welcome-file处理的差异
- mysql-(四)-关联查询
- git学习笔记(一)--什么是GitHub
- git学习笔记(二)-git操作
- mysql-(五)-存储过程
- iOS-iOS异常捕获和崩溃日志
- mysql-(五)-权限
- mysql-(六)-实战练习
- struts基础内容-1-执行流程
- beautiful soap simple examples
- 今天遇到的bug。。。
- struts基础内容-2-配置详解
- struts-基础内容-3-action开发,路径,常量
- struts-基础内容-4-全局配置