美文网首页iOS 原理篇
iOS NSException异常处理

iOS NSException异常处理

作者: 送我迷迭香 | 来源:发表于2021-05-23 22:36 被阅读0次

    为什么使用NSException

    在平常的开发过程中,由于代码不严谨或者没有对参数做校验处理会造成程序crash,所以需要去监控项目中的crash并及时解决。NSException是针对OC类异常捕获的一套机制。

    如何使用NSException

    NSException的结构如下:

    @interface NSException : NSObject <NSCopying, NSSecureCoding> {
        @private
        NSString        *name;
        NSString        *reason;
        NSDictionary    *userInfo;
        id          reserved;
    }
    

    用法:

    static NSUncaughtExceptionHandler* fb_previousUncaughtExceptionHandler;
    
    void installUncaughtExceptionHandler(void){
        fb_previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
        NSSetUncaughtExceptionHandler(&uncaughtHandleException);
    }
    void uninstallUncaughtExceptionHandler(void){
        if(fb_previousUncaughtExceptionHandler){
            NSSetUncaughtExceptionHandler(fb_previousUncaughtExceptionHandler);
        }
    }
    void uncaughtHandleException(NSException *exception)
    {
        // 异常的堆栈信息
        NSArray *stackArray = [exception callStackSymbols];
        // 出现异常的原因
        NSString *reason = [exception reason];
        // 异常名称
        NSString *name = [exception name];
        NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];
        NSLog(@"%@", exceptionInfo);
        if (fb_previousUncaughtExceptionHandler != NULL)
        {
            fb_previousUncaughtExceptionHandler(exception);
        }
    }
    
    

    我们用NSMutableArray为例,添加一个空的object,看看会怎么样

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        installUncaughtExceptionHandler();
    
        NSMutableArray *array = [NSMutableArray array];
        NSString *nilStr = nil;
        [array addObject:nilStr];
       }
    

    程序运行后会崩溃,通过断点可以看到捕获到的异常原因以及相关堆栈信息


    image.png

    如何避免潜在的crash

    由于OC中很多崩溃都是来源于没有对入参进行判断,所以调用方法时对入参进行判断就能提前拦截崩溃。常见的处理方式有hook和安全接口两种,下面以hook为例。

    #import "NSMutableArray+fbSafe.h"
    #import <objc/runtime.h>
    
    @implementation NSMutableArray (fbSafe)
    
    + (void)load {
        [self swizzMethodOriginalSelector:@selector(addObject:)
                         swizzledSelector:@selector(avoidCrashAddObject:)];
    }
    
    + (void)swizzMethodOriginalSelector:(SEL)originalSelector
                       swizzledSelector:(SEL)swizzledSelector {
        //获取当前类
        Class arrayMClass = NSClassFromString(@"__NSArrayM");
        //获取系统的添加元素的方法
        Method originalMethod = class_getInstanceMethod(arrayMClass, originalSelector);
        //获取自定义添加元素的方法
        Method swizzledMethod = class_getInstanceMethod(arrayMClass, swizzledSelector);
        BOOL didAddMethod = class_addMethod(arrayMClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        //方法交换
        if (didAddMethod) {
            class_replaceMethod(arrayMClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    }
     
    - (void)avoidCrashAddObject:(id)object {
     
        @try {
            //可能出现异常的方法 比如数组不能添加空对象 所以addObject可能会出现异常
            [self avoidCrashAddObject:object];//本质是调用addObject
        
        } @catch (NSException *exception) {
            //捕捉到异常后对该异常进行处理
            NSLog(@"\n异常名称:%@\n异常原因:%@ \n异常堆栈信息:%@",exception.name,exception.reason,exception.callStackSymbols);
        } @finally {
            //这里的代码一定会执行,可以进行相应的操作
        }
    }
    
    @end
    

    再次运行程序,发现没有崩溃,控制台打印出了异常信息

    image.png

    我们可以把拿到的异常信息保存文件到沙盒或者上报到服务器,方便问题排查统计。

    总结

    使用NSException可以方便我们去收集app的crash信息,并对一些常见的crash问题提前做出处理,使app能够平稳运行。不足的是NSException只能针对OC类异常进行捕获,如果是Signal类异常就无能为力了,所以还要专门做Signal类处理。

    相关文章

      网友评论

        本文标题:iOS NSException异常处理

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