2

Here's the situation. Let's say I have a class called MYFoo. Here's it's initializer:

-init
{
  self = [super init];
  if (self)
  {
    // during initialization, something goes wrong and an exception is raised
    [NSException raise ...];
  }
  return self;
}

Now somewhere else I want to use a MYFoo object, so I use a common pattern:

MYFoo *foo = [[[MYFoo alloc] init] autorelease];

But what's going to happen is that, even if there's a try/catch around the 2nd part, a MYFoo object is going to be allocated, the exception will be thrown, the autorelease missed, and the uninitialized MYFoo object will leak.

What should happen here to prevent this leak?

stevex
  • 5,589
  • 37
  • 52
  • 2
    It's hard to tell, but I don't think you should be using exceptions this way. In Cocoa, you only use exceptions for programmer errors--it's rare that you have to recover from an exception, especially one during object initialization. Initialization should generally either succeed or return `nil`. Try putting your dangerous operations in separate methods and using a combination of signaling and `NSError` to deal with failure. – shosti Feb 15 '11 at 16:04

2 Answers2

8

The Apple Docs say the best practice is not to throw.

Handling Initialization Failure

In general, if there is a problem during an initialization method, you should call [self release] and return nil.

If you need to know what happened, you can init the object and have some kind of internal state that gets checked by the caller to ensure the object is usable.

Donald Byrd
  • 7,668
  • 4
  • 33
  • 50
1

ongle is 100% right. But if you do need to throw an exception and you do want to catch it somewhere (as opposed to just bailing out of the application), you can wrap your initialisation in @try { ... } @finally { ... }

-init
{
   self = [super init];
   if (self)
   {
       @try
       { 
           // during initialization, something goes wrong and an exception is raised
           @throw...
       }
       @finally
       {
           [self release]; 
       }
   }
   return self;
}

If you do the above, you should document that the init method can throw an exception because otherwise other users of your code will expect the default behaviour and possibly not write exception safe code.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Bottom line: Do **not** throw exceptions for recoverable errors. JeremyP's code is technically correct, but semantically against the patterns of Cocoa/iOS. A leak caused by an exception should be irrelevant in that the program is going to be terminated near immediately anyway. – bbum Feb 15 '11 at 17:08
  • @bbum: Which is a pity because, in theory at least, exceptions are superior to polluting the API because of having to pass references to error objects around and having to reserve special return values to indicate errors. – JeremyP Feb 16 '11 at 08:44
  • @bbum: Also, given the Cocoa pattern, it would be better if the default behaviour for a Cocoa application were to terminate when it detects an unhandled exception instead of catching it in the run loop and continuing as if nothing had happened. – JeremyP Feb 16 '11 at 08:46
  • Correct; would be better to simply `abort` than `@throw`. There are many folks -- myself included -- that find exceptions to never be superior for recoverable error handling because of the complexity of multi-path execution incurred and all the special cases that have to be handled therein. I've done exception rich programming (Java, Python, others) and it was painful. Not saying the return value / error** is pretty, by any means, but it does yield *exactly one* execution path. – bbum Feb 16 '11 at 16:46