美文网首页
iOS 全局异常捕获 包括signal

iOS 全局异常捕获 包括signal

作者: FlowYourHeart | 来源:发表于2018-06-20 16:40 被阅读60次

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

    .h文件
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @interface ICBUncaughtExceptionHandler : NSObject
    {
        BOOL dismissed;
    }
    + (void)InstallUncaughtExceptionHandler;
    
    + (NSArray *)backtrace;
    
    //利用 NSSetUncaughtExceptionHandler,当程序异常退出的时候,可以先进行处理,然后做一些自定义的动作,比如直接在发生异常时给某人发送邮件 
    void UncaughtExceptionHandlers (NSException *exception);
    
    /* 使用方法
     在delegate文件里面- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions函数里面 调用下面两个方法
     
     [ICBUncaughtExceptionHandler InstallUncaughtExceptionHandler];
     NSSetUncaughtExceptionHandler (&UncaughtExceptionHandlers);
     
     */
    @end
    
    
    .m文件
    
    #import "ICBUncaughtExceptionHandler.h"
    #include <libkern/OSAtomic.h>
    #include <execinfo.h>
    
    
    
    NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
    
    NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
    NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
    
    volatile int64_t UncaughtExceptionCount = 0.0;
    const int64_t UncaughtExceptionMaximum = 10.0;
    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];
        return appInfo;
    }
    
    
    void MySignalHandler(int signal)
    {
        int64_t exceptionCount = OSAtomicIncrement64(&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];
        }
        
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];
        NSArray *callStack = [ICBUncaughtExceptionHandler backtrace];
        [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
        [[[ICBUncaughtExceptionHandler alloc] init]
         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 ICBUncaughtExceptionHandler
    
    +(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];
        [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);
        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://1320664343@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
    

    参考原文:https://blog.csdn.net/yhhwatl/article/details/34432603

    相关文章

      网友评论

          本文标题:iOS 全局异常捕获 包括signal

          本文链接:https://www.haomeiwen.com/subject/fzyxyftx.html