iOS NSException与NSSetUncaughtExceptionHandler处理异常

前言

hihi,勇敢的小伙伴儿们大家好, 了解这个是因为我在项目里集成了Bugly,即使不在开发过程,用户使用项目产生崩溃的时候也能将崩溃的原因上传到服务器上,尤其是试运营的版本,能够为后期优化App和减少Bug数提供不少方便,记得以前看安卓代码有try/catch的捕捉异常和处理异常的操作(此处存疑,记不太清了),想着iOS异常是怎么捕捉到的呢?于是有了这篇博客。

接下来跟大家一起学习一下NSSetUncaughtExceptionHandler这个类、顺便了解一下NSException的强大~

正文

1.NSSetUncaughtExceptionHandler

说明

利用NSSetUncaughtExceptionHandler可以用来处理异常崩溃。崩溃报告系统会用NSSetUncaughtExceptionHandler方法设置全局的异常处理器。

如果自定义NSSetUncaughtExceptionHandler监听事件,会导致第三方监听(如Bugly)失效,需要注意。

用法

#import "ViewController.h"

void UncaughtExceptionHandler(NSException *exception) {
    NSArray *arr = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSLog(@"\n%@\n%@\n%@",arr,reason,name);
}

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    NSArray *arr = @[@(0),@(1)];
    NSLog(@"%@",arr[2]);//模拟越界异常
    
}

@end

运行结果:

iOS NSException与NSSetUncaughtExceptionHandler处理异常_第1张图片

参考文章:NSSetUncaughtExceptionHandler处理异常

2.NSException

说明

NSException是什么?NSException是很多开发者最熟悉的陌生人,包括我在内,其实每天都跟它打过N次照面,可是仅仅是认识而已,基本不清楚它的内涵···

就像前面我们自定义输入的Exception的name reason和stack symbols,然而控制台经常输出的日志信息就是NSException产生的默认格式,一旦程序抛出异常,程序就会崩溃,控制台就会有这些崩溃日志。

iOS NSException与NSSetUncaughtExceptionHandler处理异常_第2张图片

用法

1.自定义异常崩溃

    //创建异常
    NSString *exceptionName = @"hi,我是一个异常";
    NSString *excaptionReason = @"我不开心了,所以我要让程序崩溃";
    NSDictionary *exceptionUserInfo = nil;
    NSException *exception = [NSException exceptionWithName:exceptionName reason:excaptionReason userInfo:exceptionUserInfo];
    
    //抛出异常
    @throw exception;

运行结果:

iOS NSException与NSSetUncaughtExceptionHandler处理异常_第3张图片

2.异常的简单处理

    NSMutableArray *array = [NSMutableArray array];
    NSString *nilStr = nil;
    
    @try {

        //有可能出现异常的代码,这里写的代码一定会出现问题
        [array insertObject:nilStr atIndex:0];

    } @catch(NSException *exception) {
        
        //如果@try的代码出现异常,就会执行这里的代码,也就可以在这里进行相应的操作
        NSLog(@"%@",exception.reason);
        
        //如果想要抛出异常就执行如下代码,程序就会崩溃,便于调试
        //@throw exception;
        
    } @finally {
        
        //这里的代码一定会执行
        NSLog(@"已成功处理异常");
        
    }

运行结果:

3.防止潜在的崩溃风险

如果你并不知道程序运行到那里会出现异常,或者说对于Foundation框架里有非常多常用的方法有导致崩溃的潜在危险,那么该如何拦截潜在的异常风险,并进行相应的处理,防止崩溃的出现呢?

解决办法是:

  • 利用iOS的runtime特性和catalog添加新方法,替换掉系统的存在异常风险的方法。
  • 利用异常捕获防止程序崩溃,并进行相应处理。

我们以“在数组中插入一个空对象”的崩溃异常为例,进行相应的处理。

首先给NSMutableArray加一个名为Extension的Category

NSMutableArray+Extension.m

#import "NSMutableArray+Extension.h"
#import 

@implementation NSMutableArray (Extension)

+ (void)load {
    Class arrayMClass = NSClassFromString(@"__NSArrayM");
    
    //获取系统的添加元素的方法
    Method addObject = class_getInstanceMethod(arrayMClass, @selector(addObject:));
    
    //获取我们自定义添加元素的方法
    Method avoidCrashAddObject = class_getInstanceMethod(arrayMClass, @selector(avoidCrashAddObject:));
    
    //将两个方法进行交换
    //当你调用addObject的时候,其实就是调用avoidCrashAddObject
    //当你调用avoidCrashAddObject的时候,其实就是调用addObject
    method_exchangeImplementations(addObject, avoidCrashAddObject);
}

- (void)avoidCrashAddObject:(id)object {

    @try {
        
        //可能出现异常的方法 比如数组不能添加空对象 所以addObject可能会出现异常
        [self avoidCrashAddObject:object];//本质是调用addObject
    
    } @catch (NSException *exception) {
    
        //捕捉到异常后对该异常进行处理
        NSLog(@"\n异常名称:%@\n异常原因:%@",exception.name,exception.reason);
    
    } @finally {
    
        //这里的代码一定会执行,可以进行相应的操作
    
    }
}

@end

然后在ViewController.m中测试一下

    NSMutableArray *array = [[NSMutableArray alloc] init];
    NSString *nilStr = nil;
    //测试异常是否执行
    [array addObject:nilStr];

运行结果:

程序没有崩溃,即没有抛出异常,继续运行了下去,并按照我们自定义的方法输出了异常名称及其原因。

有效的规避了数组插入空对象的异常崩溃,为程序稳定性做出了巨大贡献。✿✿ヽ(°▽°)ノ✿

关于这个的Demo放在我的github自定义异常处理上。

我已经在文章里将代码写的很全了,应该用不到,不过大家需要的话可以下载~

 

今日写完这篇博客有些疲累,有大神已经把常见崩溃处理了,我还没有学习,故此先送上原文地址给各位小伙伴儿

iOS runtime实用篇--和常见崩溃say good-bye!欢迎大家跟我一起探讨~

有问题烦请联系我,多谢!

 

你可能感兴趣的:(iOS)