为什么使用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类处理。
网友评论