zoukankan      html  css  js  c++  java
  • iOS捕获异常的处理

    demo地址:https://github.com/easonoutlook/UncaughtExceptionHandler

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

    #import <UIKit/UIKit.h>

    @interface UncaughtExceptionHandler : NSObject

    {

    BOOL dismissed;

    }

    @end

    void InstallUncaughtExceptionHandler();

    然后在.mm文件实现InstallUncaughtExceptionHandler(),如下:

    void InstallUncaughtExceptionHandler()

    {

    signal(SIGABRT, MySignalHandler);

    signal(SIGILL, MySignalHandler);

    signal(SIGSEGV, MySignalHandler);

    signal(SIGFPE, MySignalHandler);

    signal(SIGBUS, MySignalHandler);

    signal(SIGPIPE, MySignalHandler);

    }

    这样,当应用发生错误而产生上述Signal后,就将会进入我们自定义的回调函数MySignalHandler。为了得到崩溃时的现场信息,还可以加入一些获取CallTrace及设备信息的代码,.mm文件的完整代码如下:

    #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;

    @implementation UncaughtExceptionHandler

    + (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];

    }

    }

    @end

    NSString* getAppInfo()

    {

        NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\nUDID : %@\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;

    }

    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];

    }

    void InstallUncaughtExceptionHandler()

    {

    signal(SIGABRT, MySignalHandler);

    signal(SIGILL, MySignalHandler);

    signal(SIGSEGV, MySignalHandler);

    signal(SIGFPE, MySignalHandler);

    signal(SIGBUS, MySignalHandler);

    signal(SIGPIPE, MySignalHandler);

    }

    在应用自身的 didFinishLaunchingWithOptions 前,加入一个函数:

    - (void)installUncaughtExceptionHandler

    {

    InstallUncaughtExceptionHandler();

    }

    最后,在 didFinishLaunchingWithOptions 中加入这一句代码就行了:

    [self InstallUncaughtExceptionHandler];

    现在,基本上所有崩溃都能Hold住了。崩溃时将会显示出如下的对话框:

    这样在崩溃时还能从容地弹出对话框,比起闪退来,用户也不会觉得那么不爽。然后在下次启动时还可以通过邮件来发送Crash文件到邮箱,这就看各个应用的需求了。

  • 相关阅读:
    CSS页面渲染优化属性will-change
    前端自动化构建工具-yoman浅谈
    【积累】如何优雅关闭SpringBoot Web服务进程
    SpringCloud Eureka Client和Server侧配置及Eureka高可用配置
    SpringBoot返回html页面
    MySQL8主从配置
    使用Arrays.asList抛出java.lang.UnsupportedOperationException
    SpringMVC+Mybatis+MySQL8遇到的问题
    MySQL5.6启用sha256_password插件
    Base64简单原理
  • 原文地址:https://www.cnblogs.com/easonoutlook/p/2835979.html
Copyright © 2011-2022 走看看