Should I put [pool drain] in a @finally clause?
I am starting to program in Object开发者_StackOverflow社区ive-C after many years of Java development. One issue I am struggling a bit with is memory management. In particular, most examples in books and online don't seem to take into account memory leaks due to exceptions. For example consider the following method:
-(void) doSomething
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
// Allocate some autoreleased objects here
NSString *data = [NSString stringWithString@"Hello"];
// Do some work, exception could be thrown
[PotentialExeptionThrower maybeThrowException];
// Clean up autorelease objects
[pool drain];
}
Shouldn't the above be rewritten as follows to prevent any potential memory leaks?
-(void) doSomething
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
@try {
// Allocate some autoreleased objects here
NSString *data = [NSString stringWithString@"Hello"];
// Do some work, exception could be thrown
[PotentialExeptionThrower maybeThrowException];
} @finally {
// Clean up autorelease objects
[pool drain];
}
}
Is the code above less efficient because of the @try-@catch?
Thanks!
As a general thing: no, you should not. The pool should not be drained at all on the exception path. Instead, it will be automatically drained when its parent pool is. (Doing it the way you are will lead to a crash if code further up the call hierarchy attempts to inspect the exception, because your finally clause will have released it.)
The exception is when you’re in charge of the outermost pool, which is most likely to happen in multithreaded code. In this case, if you expect your code to generate exceptions, you must catch the exception and either squash it or handle it, and then drain the autorelease pool. (Not doing this will lead to a crash. This can actually be considered acceptable if you aren’t raising any exceptions on your own, because any exception raised by the system frameworks indicates a programming error and leaves your program in an undefined state.)
That's explained in this Apple document. You don't have to do that. The point is that if the lower pool is drained, the undrained upper pools are also automatically drained. So yes, the releasing of the autoreleased object would be delayed by exception, but they will be eventually released.
It can be illustrated thus:
NSAutoreleasePool* A = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool* B = [[NSAutoreleasePool alloc] init];
.... do many things. Put things in B ...
NSAutoreleasePool* C = [[NSAutoreleasePool alloc] init];
.... do many things. Put things in C ...
[A drain]; // it automatically drains B and C, too!
Note that you don't have to set up the pool so often. Setting it up in every method is surely an overkill. Usually you don't start adding local pools unless real measurements by Instruments suggest there are so many autoreleased objects held in a method.
Note that exceptions in Mac OS X and iOS applications are generally reserved only to indicate unrecoverable, programmer, error.
If you throw an exception through the frameworks, the behavior is undefined.
Exceptions are not to be used for flow control.
(As someone else stated -- don't release the pool in the exception case. The parent pool will clean it up.)
No you should not. I know your experience is tainted by years of Java, you need to change how you approach exception.
Exceptions, as the name suggest, are not part of normal program flow in Cocoa apps. Exceptions are used for catching errors made by the developer, YOU, and should be taken care of before your apps ships. Cover your data model with unit tests to smoke out every last one of them.
For example NSInvalidArgumentException
is not something you should ever sneak into production code. Validate user-inputs and cover edge cases of your logic with unit tests!
For errors that are not your fault, and that you can recover from you should instead use NSError
instances. They have the nice property of localized descriptions suitable for end users. NSError
also have functionality in place to handle suitable recovery options, such as "Cancel" or "Retry".
I have written a longer blog-post on Exceptions and Errors on iOS here: http://blog.jayway.com/2010/10/13/exceptions-and-errors-on-ios/
Autorelease pools are special–the normal "if you new/alloc/copy, you must release" doesn't exactly apply. When you create an autorelease pool, it's placed on a special stack. If you neglect to drain a pool, it will be drained if any pools before it in the stack are drained. Draining in a catch block isn't necessary, but an exception may delay pool draining. Read "Scope of Autorelease Pools and Implications of Nested Autorelease Pools" for details.
精彩评论