• IOS从容崩溃


    文/donglin

    注:鉴于多名网友对文中代码提出的质疑,小编联系了作者,迅速给予更正并更新。感谢大家的监督与支持!

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

    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文件到邮箱,这就看各个应用的需求了。

    原文来自:触控科技博客

    程序崩溃,总是难免的事儿,无论对于谁,一个应用用着用着,突然退出了,总是件很尴尬的事儿,想想做人最基本的礼貌:你可以对我不感冒,你总不能不甩我吧?这个问题,研究好长时间了,最后也只能用系统自带的NSSetUncaughtExceptionHandler临时捕获错误,然后再次打开的时候提示用户,上次崩溃了,问要不要发个邮件给开发者,一直想研究怎么崩溃的时候就给个提示,然后“从容地”崩溃。这不cocoachina有了这篇儿文章:如何让IOS应用从容地崩溃
    不过上面的文章说的太细,其实对于咱们搞iOS的人来说,一个demo胜一篇儿文章,so here it is:
    bengkui
    运行效果图:

  • 相关阅读:
    典型漏洞归纳之上传漏洞
    典型漏洞归纳之解析漏洞
    Python学习目录
    MySQL数据库优化的八种方式
    深度剖析Flask上下文管理机制
    算法十大排序(含动图)
    设计模式代码实例
    设计模式
    数据结构
    算法基础
  • 原文地址:https://www.cnblogs.com/ligun123/p/2800794.html
Copyright © 2020-2023  润新知