发生在Objective-C层的奔溃异常,称为:OC Exception
1. NSException介绍
如果说你对NSException这个类不了解,那这下面这张图的输出内容在开发过程中肯定经常和你见面:
附导致上图中抛出错误的代码:
NSMutableArray *array = [NSMutableArray array];
NSString *strObj = nil;
[array addObject:strObj];
这样的错误是致命的,也就说在不做任何容错的情况下,程序直接Crash,也就是俗称”闪退“,解决这类iOS提供的SDK导致的崩溃异常可以使用NSSetUncaughtExceptionHandler来进行处理,附GitHub链接:
https://github.com/minibear0523/UncaughtExceptionHandler
但App奔溃的的原因还有内存访问错误、重复释放等问题,这些异常NSSetUncaughtExceptionHandler是无法处理的,因为这类错误发送的是SIGNAL。
2. NSException处理
苹果提供了NSError错误处理和NSException异常处理两种机制,NSError用来处理程序中可恢复的错误(通常情况下程序可以继续运行),而NSException用来处理不可恢复的错误(会导致程序终止的严重异常)
在没有研究NSException之前,苹果开发一直有个令我非常疑惑的地方,就是Java中经常使用的try{****} catch{****},为什么在iOS代码中几乎见不到?这个得益于ARC自动内存管理,在大多数时候开发者并不需要手动管理内存(并不是绝对安全),但是如果你面对某段有潜在奔溃风险的代码,则可以使用try catch来捕捉异常:
@try {
//放有可能会出现异常错误的代码
}
@catch (NSException *exception) {
//只有try块中的代码出现了异常才会执行,用于匹配处理抛出的异常
}
@finally {
//无论是否抛出异常,都会执行的代码
}
整个执行过程会有以下几种情况:
- try块代码没有发生异常,则程序会执行try块内、finally块内、finally块之后的代码;
- try块代码发生了异常,并且有catch捕捉匹配的异常,则程序在try块内发生异常后剩下的代码将不再执行,跳转到catch块内并抛出NSException对象;
- try块代码发生了异常,并且有catch没有能匹配捕捉,则程序会执行try和finally块中的代码,并将异常返回给调用者,finally块之后的代码也不执行。
通过try catch方法可以解决很多问题,上面addObject插入nil对象的奔溃也可以被妥善解决:
@try {
[array addObject:strObj];
}
@catch (NSException *exception) {
//打印exception输出:
//exception.name = NSInvalidArgumentException
//exception.reason = *** -[NSArrayM insertObject:atIndex:]: object cannot be nil
//还可以通过exception.callStackSymbols查看函数堆栈,发现对应异常错误发生在哪个类中的哪一行
}
@finally {
}
3. Foundation常见奔溃异常
Foundation是我们在开发过程中最常使用的框架,与CoreFoundation不同Foundation提供的是Objective-C接口,他主要提供了:数据类型(数组、字典、集合等)、字符串、时间、日期、URL、线程、RunLoop等。
这也造成了很多异常的发送,比如:
- 数组越界
- 可变数组插入空对象
- KVO在addObserver后不remove
- 调用对象方法找不到selector对应的函数时,消息转发
可以利用Runtime特性在Category使用新函数替换掉系统的存在异常风险的方法
4. 在发布到App Store的版本中,通过上述手段捕捉的异常如何得知?
由于我们将异常捕获了,所以程序不会产生奔溃现象,那么在这样的情况下作为开发者如何收集这些被捕获的异常呢?显然像友盟、Bugly这样的第三方SDK根本不管用了,其实完全可以自给自足,我是通过腾讯Mars框架中的Xlog来实现的,并且人家的框架生成的.xlog文件还支持非对称加密,保证了在网络传输过程中即使被截获也不用担心泄漏。
附上Mars的GitHub链接:
https://github.com/Tencent/mars
如果本文对你有所帮助,记得点击一下喜欢哈