MRC手动管理引用计数时
@try {
EOCSomeClass *object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
[object release]
}
@catch(...){
NSLog(@"Whoops, there was an error. Oh well...")
}
乍一看上去似乎没有问题,但如果doSomethingThatMayThrow抛出异常了呢?由于异常会令执行过程终止并跳出catch块,因而其后的那行release代码不会运行。在这种情况下,如果代码抛出异常,那么对象就泄漏了。这么做不好。解决办法使用@finally块,无论是否抛出异常,其中的代码都会保证运行,且只运行一次。代码可更改为:
@try {
EOCSomeClass *object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
}
@catch(...){
NSLog(@"Whoops, there was an error. Oh well...")
}
@finally {
[object release]
}
注意,由于@finally块也引用object对象,所以必须把它从@try块里移到外面去。如果@try逻辑非常复杂,含有多条语句,那么很容易就会因为忘记某个对象而导致内存泄漏
在ARC环境下
@try {
EOCSomeClass *object = [[EOCSomeClass alloc] init];
[object doSomethingThatMayThrow];
}
@catch(...){
NSLog(@"Whoops, there was an error. Oh well...")
}
由于ARC中不能调用release,所以无法像MRC手动管理引用计数那样把释放操作移到@finally块中。你可能认为这种情况ARC自然会处理。但实际上不会自动处理,因为这样做需要加入大量样板代码,以便跟踪待清理的对象,从而抛出异常是将起释放。可是,这段代码会严重影响运行期的性能,即便在不抛出异常时也是如此。而且添加进来的额外代码还会明显增加应用程序的大小。这些副作用都不甚理想。
虽然默认状况下未开启,但ARC依然能生成这种安全处理异常所用的附加代码。-fobjc-arc-exception 这个编译标志用来开启此功能。其默认不开启的原因是:在Objective-C代码中,只有应用程序必须因异常状况而中止是才应抛出异常。因此,如果应用程序即将终止,那么是否还会发生内存泄漏就已经无关紧要了。在应用程序必须立即终止的情况下,还去添加安全处理异常所用的附加代码是没有意义的。
网友评论