Travis Travis - 2 months ago 10
Objective-C Question

Objective C - What happens if an exception occurs before autorelease is called in the return statement?

I see examples all the time of autorelease being called at the point the object is returned from a method but isn't this risking a memory leak if an exception occurs between allocating the memory and returning the object?

NSString *newString = [[NSString alloc] init];
//Do some other stuff which causes exception
return [newString autorelease];


So to make it a bit clearer what I mean:

@autoreleasepool {
try {
SomeObject *newObject = [SomeClass generateAutoreleaseObject];
} @catch (NSException *e) {
//log exception and carry on.
}
}


If generateAutoreleaseObject is as follows is there a memory leak?

(SomeObject*)generateAutoreleaseObject {
SomeObject *newObject = [[SomeObject alloc] init];
//Do some other stuff which causes exception
return [newObject autorelease];
}


If so do the foundation classes that return autorelease objects handle this and I can sleep easy in my bed tonight safe in the knowledge that stringWithContentsOfURL will not leave any memory floating about because it had a problem reading from the URL?

Answer

but isn't this risking a memory leak if an exception occurs between allocating the memory and returning the object?

Yes, it is. The exception moves execution directly from the point where it's raised to the handler. This is explicitly called out in an example in Apple's Cocoa Exceptions Guide:

 - (void)doSomething {
     NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:0];
     [self doSomethingElse:anArray];
     [anArray release];
 }

The problem here is obvious: If the doSomethingElse: method throws an exception there is a memory leak.

So, how can you get around this? Well, the exception handling syntax includes an @finally block, which is run after the @try and after any @catch is made.

If an exception is possible in your -generateAutoreleaseObject, you could use a local exception handler with an @finally block to make sure that resources are cleaned up, and, if needed, also re-throw the exception. But see below about internal Cocoa code!

If generateAutoreleaseObject is as follows is there a memory leak?

Yes. It's also worth noting that ARC wouldn't necessarily help here. Again, control jumps from the point of throwing to the point of handling. ARC can't reasonably ensure that it can clean up in that situation by default. (You can see at that link, though, that there is a compiler option you can use.)

If so do the foundation classes that return autorelease objects handle this

Not really. No guarantees are made about the state of framework internals if your exception jumps back over stack frames involving them. This is hard to avoid, and that's why the mantra is that exceptions aren't for recoverable errors in Cocoa.

For more, here's two Apple runtime engineers on this topic: Objective-C ARC and longjmp