4

I find myself writing code like this to achieve exception safe code:

Container* container = [Container new];
@try {
    while(someCondition) {
        ElementType* value = [someObject createObjectFromStorage];
        [container add:value]; // container retains object
        [value release];
    }

    [_container release];
    _container = [container retain];
} @finally {
    [container release];
}

Is there some other, and more succinct pattern to follow in Objective-C?

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51

2 Answers2

8

If you're just looking to make sure you've released your objects, autorelease is probably sufficient. You might also look into the new Automatic Reference Counting option in Xcode 4.2.

Objective-C does not, in general, lend itself to RAII because all Objective-C objects are allocated on the heap. This means that the lifetime of objects is not explicitly tied to any particular stack frame, and thus you cannot rely on the object being deallocated at the end of the method that allocated it.

You should also be aware that the Cocoa frameworks use exceptions only to indicate programmer error, not anticipated error conditions. From Apple's "Exception Programming Guide" documentation:

Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.

If you have an existing body of code (such as third-party library) that uses exceptions to handle error conditions, you may use the code as-is in your Cocoa application. But you should ensure that any expected runtime exceptions do not escape from these subsystems and end up in the caller’s code. For example, a parsing library might use exceptions internally to indicate problems and enable a quick exit from a parsing state that could be deeply recursive; however, you should take care to catch such exceptions at the top level of the library and translate them into an appropriate return code or state.

In fact, because exceptions are intended to be used only for exceptional cases, by default the newly introduced Automatic Reference Counting will intentionally leak objects when throwing an exception:

The standard Cocoa convention is that exceptions signal programmer error and are not intended to be recovered from. Making code exceptions-safe by default would impose severe runtime and code size penalties on code that typically does not actually care about exceptions safety. Therefore, ARC-generated code leaks by default on exceptions, which is just fine if the process is going to be immediately terminated anyway. Programs which do care about recovering from exceptions should enable the option.

Programming with the Cocoa frameworks will go much better if you adhere to the idioms of that framework. That means using exceptions only for programmer errors and handling anticipated runtime errors with NSError. Most Cocoa programmers never worry about writing exception-safe code, because their code doesn't throw exceptions in the first place. You may do well to follow suit.

BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • +1 I was half-way through writing exactly the same posting, including the same quotes :D It is extremely rare to throw exceptions in ObjC, and you should expect the program to terminate shortly afterwards. – Rob Napier Dec 07 '11 at 21:47
  • 2
    I come from a C++ background, and I must say I find Apple's approach to exceptions a bit "odd" to put it mildly. They basically say it's ok to use exceptions, as long as you "keep it to yourself". I can see the reason for a minimal approach to exceptions, as the code quickly become cludgy given that there is no real mechanism for the RAII pattern. On the other hand, code can become rather cludgy with checking return values all the time as well... Oh well. New house, new rules! :) – Jörgen Sigvardsson Dec 07 '11 at 22:51
  • By the way, down the road I am going to do lots of networking. In the C# and C++ world (parts of it anyway), exceptions are used extensively to indicate error conditions, such as broken network connectivity, etc. What's the typical approach to such error conditions in Cocoa (iOS mostly, I don't see OS X on the horizon any time soon)? Checking if a return (or out parameter) is `NSError*`? – Jörgen Sigvardsson Dec 07 '11 at 22:55
  • The typical approach is to check the `NSError`. If a method takes an `NSError**` and the return value is nil, look at the `NSError` to figure out what happened. (If the return value is not nil, the error param should be ignored.) Some parts of the framework (such as `NSURLConnection`) will pass you an `NSError` in a callback or delegate method instead. – BJ Homer Dec 07 '11 at 23:04
  • Also, check out the [Error Handling Programming Guide](http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html) – BJ Homer Dec 07 '11 at 23:08
1

Autorelease is the standard pattern.

Container* container = [[Container new] autorelease];
while(someCondition) {
    ElementType* value = [someObject createObjectFromStorage];
    [container add:value]; // container retains object
    [value release];
}

[_container release];
_container = [container retain];

This only applies to memory management, however; it's not a full replacement for RAII. There's no commonly-used pattern in Objective-C that completely replaces RAII, although you could create such a pattern in your own codebase using blocks (or maybe __attribute__((cleanup))).

While we're talking about patterns, by the way… the general pattern is that any method that doesn't start with the word new, alloc, copy, or mutableCopy returns an autoreleased object. If you changed your hypothetical method to -objectFromStorage and made it follow that pattern, your loop would be even more concise:

while(someCondition) {
    [container add:[someObject objectFromStorage]];
}
John Calsbeek
  • 35,947
  • 7
  • 94
  • 101
  • Note that sometimes, you may also want to release objects immediately... Good answer though. – Macmade Dec 07 '11 at 21:29
  • Yes: this isn't a full replacement for RAII, it only helps for memory allocations that you don't mind not freeing immediately. – John Calsbeek Dec 07 '11 at 21:31
  • @Macmade - why do you want to release the objects immediately? Are you managing some resource other than memory? Apple explicitly advise against trying to tie scarce resource management to object lifetimes, which is one of the things (other than exception safety) that RAII is all about (see [Don't use dealloc to manage scarce resources](http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW13)) – bacar Jul 27 '12 at 18:02