iOS try-catch memory leak详解

iOS try-catch memory leak详解

无论多优秀的客户端的工程师,都架不住服务端不经意间给你吐回的异常数据,怎么能够尽量的避免被坑呢?一个可能的方式,在使用数据之前做好类型校验;另一个,在涉及一些可能会越界或者自己不放心的地方,加一下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呢?

只要使用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中做好清理工作。

你可能感兴趣的:(ios开发)