52个有效方法(32) - 编写“异常安全代码”时留意内存管理问
2018-09-07 本文已影响15人
SkyMing一C
异常安全代码
@try {
// 可能会出现崩溃的代码
}
@catch (NSException *exception) {
// 捕获到的异常exception
}
@finally {
// 结果处理
//无论是否抛出异常,其中的代码都保证会运行,且只运行一次。
}
-
处理异常代码块,在这里如果try里出现异常,他会执行finally块里的代码。
-
在这个过程里
-
MRC:
手动在finally里执行release,释放对象 -
ARC:
不会自动添加,需要开启编译器标志(-fobjc-arc-exceptions)(开启会导致占用大内存,影响运行时性能)
-
例子
@try {
// 1
[self tryTwo];
}
@catch (NSException *exception) {
// 2
NSLog(@"%s\n%@", __FUNCTION__, exception);
// @throw exception; // 这里不能再抛异常
}
@finally {
// 3
NSLog(@"我一定会执行");
}
// 4
// 这里一定会执行
NSLog(@"try");
//tryTwo方法代码
- (void)tryTwo
{
@try {
// 5
NSString *str = @"abc";
[str substringFromIndex:111]; // 程序到这里会崩
}
@catch (NSException *exception) {
// 6
// @throw exception; // 抛出异常,即由上一级处理
// 7
NSLog(@"%s\n%@", __FUNCTION__, exception);
}
@finally {
// 8
NSLog(@"tryTwo - 我一定会执行");
}
// 9
// 如果抛出异常,那么这段代码则不会执行
NSLog(@"如果这里抛出异常,那么这段代码则不会执行");
}
-
如果6抛出异常,那么执行顺序为:1->5->6->8->3->4
-
如果6没抛出异常,那么执行顺序为:1->5->7->8->9->3->4
-
把捕获到的异常发送至开发者的邮箱
//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
return YES;
}
void UncaughtExceptionHandler(NSException *exception) {
/**
* 获取异常崩溃信息
*/
NSArray *callStack = [exception callStackSymbols];
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *content = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[callStack componentsJoinedByString:@"\n"]];
/**
* 把异常崩溃信息发送至开发者邮件
*/
NSMutableString *mailUrl = [NSMutableString string];
[mailUrl appendString:@"mailto:test@qq.com"];
[mailUrl appendString:@"?subject=程序异常崩溃,请配合发送异常报告,谢谢合作!"];
[mailUrl appendFormat:@"&body=%@", content];
// 打开地址
NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}
为什么iOS很少使用try catch
-
Apple虽然同时提供了错误处理(NSError)和异常处理(exception)两种机制,但是Apple更加提倡开发者使用NSError来处理程序运行中可恢复的错误。而异常被推荐用来处理不可恢复的错误。
-
try catch无法捕获UncaughtException,而oc中大部分crash如:内存溢出、野指针等都是无法捕获的,而能捕获的只是像数组越界之类(这真心需要catch么?),所以try catch对于oc来说,比较鸡肋。
要点
-
捕获异常时,一定要注意将try块内所创立的对象清理干净。
-
在默认情况下,ARC不生成安全处理一次所需的清理代码。开启编译器标志后(-fobjc-arc-exceptions),可生成这种代码,不过会导致应用程序变大,而且会降低运行效率。