Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 478|回复: 0

iOS捕获异常的处理

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-20 13:10:43 | 显示全部楼层 |阅读模式

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

     }

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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-12 16:15 , Processed in 0.056782 second(s), 28 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表