zoukankan      html  css  js  c++  java
  • 异常捕获拒绝闪退 让应用从容的崩溃UncaughtExceptionHandler

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

    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 : %@ %@(%@) Device : %@ OS Version : %@ %@ ",

          [[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. "

                             @"%@", 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. "

            @"%@ %@", 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);

  • 相关阅读:
    Centos7创建用户su登录后显示为 bash-4.1$
    winserver 搭建 Citrix License 许可服务器
    Centos7扩展存储空间
    CentOS7配置crate集群
    CentOS7安装cratedb
    初探Vue
    浅谈web攻防
    响应式布局
    JavaScript正则表达式,你真的知道?
    15个关于Chrome的开发必备小技巧[译]
  • 原文地址:https://www.cnblogs.com/zxykit/p/5157894.html
Copyright © 2011-2022 走看看