【IOS】异常捕获 拒绝闪退 让应用从容的崩溃 UncaughtExceptionHandler

来源:互联网 发布:数据泄漏防护系统 破解 编辑:程序博客网 时间:2024/04/29 13:33

        虽然大家都不愿意看到程序崩溃,但可能崩溃是每个应用必须面对的现实,既然崩溃已经发生,无法阻挡了,那我们就让它崩也崩得淡定点吧。

IOS SDK中提供了一个现成的函数 NSSetUncaughtExceptionHandler 用来做异常处理,但功能非常有限,而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了,因为这种错误它抛出的是Signal,所以必须要专门做Signal处理。首先定义一个UncaughtExceptionHandler类,代码如下:

#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface UncaughtExceptionHandler : NSObject{    BOOL dismissed;}+(void) InstallUncaughtExceptionHandler;@end
//利用 NSSetUncaughtExceptionHandler,当程序异常退出的时候,可以先进行处理,然后做一些自定义的动作,比如下面一段代码,就是网上有人写的,直接在发生异常时给某人发送邮件,</span>void UncaughtExceptionHandlers (NSException *exception);
#import "UncaughtExceptionHandler.h"#include <libkern/OSAtomic.h>#include <execinfo.h>NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";volatile int32_t UncaughtExceptionCount = 0;const int32_t UncaughtExceptionMaximum = 10;const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;NSString* getAppInfo(){    NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\n",      [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],      [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],      [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],      [UIDevice currentDevice].model,      [UIDevice currentDevice].systemName,      [UIDevice currentDevice].systemVersion];    //    [UIDevice currentDevice].uniqueIdentifier];    NSLog(@"Crash!!!! %@", appInfo);    return appInfo;}void MySignalHandler(int signal){  int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);  if (exceptionCount > UncaughtExceptionMaximum)  {    return;  }    if(signal==11)    {//比较坑爹的是 我遇到的一个问题只有iPhone5出现问题 但是我这边测试的没有iPhone5 无法直接log  可能是内存不足 果然 删除几个应用就可以了 所以加了这句
UIAlertView * tip2 = [[UIAlertView alloc]initWithTitle:@"可能原因:key" message:@"内存不足" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil];        [tip2 show];        [tip2 release];    }      NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];  NSArray *callStack = [UncaughtExceptionHandler backtrace];  [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];  [[[[UncaughtExceptionHandler alloc] init] autorelease]     performSelectorOnMainThread:@selector(handleException:)     withObject:     [NSException      exceptionWithName:UncaughtExceptionHandlerSignalExceptionName      reason:      [NSString stringWithFormat:       NSLocalizedString(@"Signal %d was raised.\n"                         @"%@", nil),       signal, getAppInfo()]      userInfo:      [NSDictionary       dictionaryWithObject:[NSNumber numberWithInt:signal]       forKey:UncaughtExceptionHandlerSignalKey]]     waitUntilDone:YES];}@implementation UncaughtExceptionHandler+(void) InstallUncaughtExceptionHandler{  signal(SIGABRT, MySignalHandler);  signal(SIGILL, MySignalHandler);  signal(SIGSEGV, MySignalHandler);  signal(SIGFPE, MySignalHandler);  signal(SIGBUS, MySignalHandler);  signal(SIGPIPE, MySignalHandler);}+ (NSArray *)backtrace{    void* callstack[128];    int frames = backtrace(callstack, 128);    char **strs = backtrace_symbols(callstack, frames);    int i;    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];    for (         i = UncaughtExceptionHandlerSkipAddressCount;         i < UncaughtExceptionHandlerSkipAddressCount +         UncaughtExceptionHandlerReportAddressCount;         i++)    {   [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];    }    free(strs);    return backtrace;}- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex{  if (anIndex == 0)  {    dismissed = YES;  }}- (void)handleException:(NSException *)exception{  UIAlertView *alert =    [[[UIAlertView alloc]      initWithTitle:NSLocalizedString(@"Unhandled exception", nil)      message:[NSString stringWithFormat:NSLocalizedString(        @"You can try to continue but the application may be unstable.\n"        @"%@\n%@", nil),               [exception reason],               [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]      delegate:self      cancelButtonTitle:NSLocalizedString(@"Quit", nil)      otherButtonTitles:NSLocalizedString(@"Continue", nil), nil]     autorelease];  [alert show];  CFRunLoopRef runLoop = CFRunLoopGetCurrent();  CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);  while (!dismissed)  {    for (NSString *mode in (NSArray *)allModes)    {      CFRunLoopRunInMode((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];  }}void UncaughtExceptionHandlers (NSException *exception) {    NSArray *arr = [exception callStackSymbols];    NSString *reason = [exception reason];    NSString *name = [exception name];    NSString *urlStr = [NSString stringWithFormat:@"mailto://1140454645@qq.com?subject=bug报告&body=感谢您的配合!<br><br><br>"                        "错误详情:<br>%@<br>--------------------------<br>%@<br>---------------------<br>%@",                        name,reason,[arr componentsJoinedByString:@"<br>"]];    NSURL *url = [NSURL URLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];    [[UIApplication sharedApplication] openURL:url];        //或者直接用代码,输入这个崩溃信息,以便在console中进一步分析错误原因    NSLog(@"1heqin, CRASH: %@", exception);    NSLog(@"heqin, Stack Trace: %@", [exception callStackSymbols]);}@end

然后在delegate文件里面- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions函数里面

[UncaughtExceptionHandler InstallUncaughtExceptionHandler];    NSSetUncaughtExceptionHandler (&UncaughtExceptionHandlers);
转自:http://blog.csdn.net/yhhwatl/article/details/34432603
0 0
原创粉丝点击