无论多优秀的客户端的工程师,都架不住服务端不经意间给你吐回的异常数据,怎么能够尽量的避免被坑呢?一个可能的方式,在使用数据之前做好类型校验;另一个,在涉及一些可能会越界或者自己不放心的地方,加一下try-catch,不过使用try-catch的时候,有一些注意事项,其中最重要的就是 memory leak。
定义一个最简单的测试类:
@interface TestObject : NSObject
@end
@implementation TestObject
- (void)dealloc{
NSLog(@"TestObject dealloc");
}
@end
随便定义一个VC,构造一个异常出来:
NSException *e = nil;
- (void)test1{
NSString*name =@"exception name";
NSString*reason =@"exception reason";
e = [NSException exceptionWithName:name reason:reason userInfo:nil];
@throw e;
}
- (void)test{
@try {
TestObject *obj = [[TestObject alloc] init];
[self test1];
}
@catch (NSException *exception)
{
}
}
在定义的VC的viewDidLoad中,调用test方法:
[self test];
会发现TestObject的dealloc没有调用,内存泄漏了,所以这种使用姿势,是不行的。
只要使用try-catch的地方,就与优雅无关,那这里的优雅,只能说是别弄出内存泄漏,其实很简单,就是将对象设计成VC的property。
@property(nonatomic, strong) TestObject *obj;
然后对test函数做一下修改:
- (void)test{
@try {
self.obj = [[TestObject alloc] init];
[self test1];
}
@catch (NSException *exception)
{
}
}
会发现TestObject的dealloc已经调用。
这样一对比也就很明显了,try-catch中的临时对象,会有内存泄漏,一定要避免这样使用。
原理其实很简单,excepiton会中断当前的流程,没有release对象的机会了,而如果是属性的话,VC再生命周期结束时,会释放它。
其实心思比较细的同学,此时已经发现了,如果我的临时变量在@try的外部声明,也不会有内存泄漏。
所以,在被迫使用try-catch的时候,一定只加在真正有可能出现crash的那段代码。
从文章 http://clang.llvm.org/docs/AutomaticReferenceCounting.html#exceptions 中了解到,我们可以通过给.m文件加上__-fobjc-arc-exceptions参数 来进行修复,防止出现 memory leak,但是这会导致编译器增加了部分碎片逻辑用于释放内存,简单的情况,可能会多出一倍的汇编代码量,对于复杂情况,编译器会插入更多的无用代码,导致生成的二进制代码变得很大,所以要慎用。
在Objective-C(ARC)中使用NSException可能会引发内存泄露,这里我们需要注意以下一些点,来避免因此带来的一些问题:
尽量使用NSError,而不是NSException来处理错误
try的范围要尽可能的小,避免很多大的内存被持有导致泄露
try内部尽量不要有临时变量,过多的try内部的临时变量,会导致很多泄露
在finnally中做好清理工作。