Breaking on Objective-C exceptions
userpic
[info]chanson
At the latest CocoaHeads meeting, some questions came up about how to break when exceptions are thrown in Objective-C. It's pretty simple to do in Xcode, and I promised to explain how here. Just open the Breakpoints window, select the Global Breakpoints group at left, and add a couple of new symbolic breakpoints. Because you'll be adding them to the Global Breakpoints group, they'll be available in all of your projects. I have a number of breakpoints like that myself, for breaking when exceptions are thrown, when messages are sent to zombie objects, and so on. It's a really useful technique to learn, and the best place to get started is probably with exceptions.

The first of your symbolic breakpoints should be -[NSException raise] — for those who don't know, that's the standard Objective-C shorthand for "the raise instance method of the NSException class" — and it will be invoked any time an Objective-C exception is raised in the "traditional" fashion, which involves instantiating NSException or a subclass and sending a -raise message to the new instance.

The second of your symbolic breakpoints will be hit whenever a language-level Objective-C exception is thrown. Newer frameworks and applications — introduced after Mac OS X 10.3 Panther — tend to use this mechanism because it was newly introduced and is a lot more mainstream as its syntax is based on Java exception syntax. Just set your breakpoint on objc_exception_throw and you'll break whenever any code you're debugging hits a @throw construct.

I call the first mechanism the "traditional" one because before the addition of Objective-C's language-level exceptions in Mac OS X 10.3 Panther, Cocoa had a framework-level exception system implemented via the low-level C setjmp(3) and longjmp(3) calls and a set of exception-handling preprocessor macros. This mechanism is often used in frameworks introduced before Mac OS X 10.3 Panther, or in application-levle code that had to be binary-compatible with Mac OS X 10.2 Jaguar. As you can see above while the @throw construct can interoperate with older code, it has Objective-C runtime bottlenecks that it goes through.

Cocoa memory management & exceptions
userpic
[info]chanson
Cocoa memory management is pretty straightforward; you only have to learn a few simple rules.

Chances are, though, that you're also going to deal with exceptions in your code. In Cocoa exceptions can complicate memory management if you're not careful. In this post I'll give you a guided tour of some of the pitfalls and show you how to avoid them.

Modern exception handling is a great mechanism for dealing with errors: It decouples the detection and handling of exceptional conditions, and automates the propagation from the point of detection to the point of handling. This results in code that's much cleaner when written, much easier to write correctly, and much easier to maintain correctly over time. If you've ever had to deal with a lot of procedural code that returns error codes that need to be checked and propagated manually, you'll know just how much more complex they make your code.

If they're so great, then, why do I say exceptions can complicate memory management? First of all, they make it easy to leak memory. Say you have a simple method that, for efficiency, avoids using autorelease for a temporary object:
- (void)leakOnException {
    NSMutableArray *array = nil;

    array = [[NSMutableArray alloc] initWithCapacity:0];
    
    [self doSomething:array];

    [array release];
}
There's a pretty obvious memory leak, if -doSomething: throws an exception. The solution is also obvious: Put the -release in a @finally block:
- (void)noLeakOnException {
    NSMutableArray *array = nil;
    
    array = [[NSMutableArray alloc] initWithCapacity:0];
    
    @try {
        [self doSomething:array];
    }
    
    @finally {
        [array release];
    }
}
Of course, your code may not be quite so simple, but the pattern will be obvious — and it applies to all resource management, not just memory. So be careful not to leak file descriptors, or forget to unlock any locks you've acquired!

Another issue you might run into is over-releasing an exception object. Exceptions are effectively out-of-band return values; in Cocoa, this means the exception object itself is autoreleased. When would this result in over-releasing the exception? When you're using an internal autorelease pool:
- (void)overReleaseException {
    NSAutoreleasePool *pool = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    [self doSomething];
    
    [pool release];
}
At first glance, this looks fine. After all, if -doSomething throws an exception, the autorelease pool will still be cleaned up when the next autorelease pool "out" in scope is released. However, this could actually happen before the exception is delivered — by the release of an outer autorelease pool in a @finally block — meaning the exception would be a zombie on delivery!

How do you fix it? One way is to make sure you don't release any pools in @finally blocks; so long as the exception is caught before any pools are released, you're safe. Another way is catch and re-throw any thrown exception in order to "promote" it to the next pool out any time you or any of your callers releases a pool in a @finally block:
- (void)promoteException {
    NSAutoreleasePool *pool = nil;
    id savedException = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    @try {
        [self doSomething];
    }
    
    @catch (id anything) {
        savedException = [anything retain];
        @throw;
    }
    
    @finally {
        [pool release];
        [savedException autorelease];
    }
}
There are several things to note about the above. The first is the use of id in both the @catch block and in the declaration of the variable we're using to save any thrown exception. You need to do this to avoid limiting your exception handling to just exceptions of class NSException. Unlike in Java, Objective-C exceptions are not required to be subclasses of any specific class or to conform to any specific protocol.

Also note that there is no argument in the @throw statement; this re-throws the caught exception exactly as caught; this is important, because Objective-C does support matching @catch blocks based on the type of the exception.

Finally — ahem — the order of operations in the @finally block is important. The whole point of the above is to retain any thrown exception across the release of the interior autorelease pool, which is the pool the exception was placed in on its way out of -doSomething, and ensure that it's autoreleased in the next pool "out" in scope. To do this, we need to make sure that the interior pool is released before autoreleasing the exception. Once the @finally block executes, exception propagation will continue and the exception will be located in the proper autorelease pool.

One other comment about this pattern: You'll need to follow it any time you are returning an object created within the scope of an interior autorelease pool. For example, if -doSomething could return an NSError you would need to ensure that it's promoted to the outer pool before being returned, like so:
- (BOOL)promoteError:(NSError **)error {
    BOOL success;
    NSAutoreleasePool *pool = nil;

    pool = [[NSAutoreleasePool alloc] init];
    
    success = [self doSomething:error];
    
    if ((success == NO) && (error != NULL)) {
        [*error retain];
    }
    
    [pool release];
    
    if ((success == NO) && (error != NULL)) {
        [*error autorelease];
    }
    
    return success;
}
The above is the same pattern as before, modulo any exception handling. The checks around the manipulation of *error are there because it's valid to pass NULL for output NSError parameters to indicate that no error should be generated, and even if NULL isn't passed, you're only allowed to touch the parameter if an error is actually generated.

One more bit about exceptions in Cocoa before I go: In general, the Cocoa frameworks themselves will not generate exceptions for anything but "programming errors." In other words, -[NSArray objectAtIndex:] may generate an exception if you ask for an object outside the range of the array, but this is the kind of exception that should never occur when an end user is using an application. The Cocoa frameworks will generally not use exceptions to do things like indicate a file doesn't exist, or a server couldn't be connected to. Other frameworks, however, may not follow the same pattern — and application code may not either. So it pays to be robust.

Update: Thanks to Michael Tsai for pointing out that I wasn't explicit enough in explaining how you might over-release an exception. I've clarified that section a bit.

Objective-C Language Enhancements
userpic
[info]chanson
There have been some Objective-C language enhancements in GCC 3.3 that are available to anyone working on Mac OS X Panther (10.3). Unfortunately they require support from the Objective-C runtime, so while they integrate correctly with code developed on earlier operating systems, code that uses them won't run on earlier operating systems. Hopefully these changes will also be supported soon by the GNU Objective-C runtime used by GNUstep; they seem very useful.

There are two major enhancements described in the GCC 3.3 release notes included with the August 2003 GCC update: A Java-style exception handling system, and Java-style synchronization blocks. Previous versions of Cocoa used preprocessor macros based on setjmp and longjmp for exception handling, and neither the language nor the framework had built-in thread synchronization primitives. (Cocoa did have several classes providing synchronization functionality though.)

The new exception handling system is modeled very closely on Java's. In the current system, you write code like this to handle exceptions:

  NS_DURING
  {
    ...
    [NSException raise:NSInvalidArgumentException
                format:@"Invalid argument '%@' to method 'blah'", argument];
    ...
  }
  NS_HANDLER
  {
    if ([[localException name] isEqualToString:NSInvalidArgumentException]) {
      // handle the exception
    } else {
      [localException raise]; // re-raise the exception
    }
  }
  NS_ENDHANDLER

Now, you would write the same thing as:

  @try {
    ...
    @throw [[[MyInvalidArgumentException alloc] init] autorelease];
    ...
  }
  
  @catch (MyInvalidArgumentException *invalidArgumentException) {
    // handle the exception
    
    @throw; // re-throw the exception if it wasn't handled
  }
  
  @catch (id allOtherExceptions) {
    // all other exceptions will be caught by this general construct,
    // or if it isn't used, they'll be caught higher up the call chain
    // just like in Java or C++
  }
  
  @finally {
    // do any cleanup that needs to be done regardless of whether
    // an exception was thrown
  }

This will lead to much cleaner code in the long run, and it's a much closer match to what many developers new to Cocoa and Objective-C are used to as well. Everybody wins!

The other feature added is Java-style serialization. It's not a complete implementation of Java's synchronized keyword; the only thing supported are blocks synchronized on a single object. But that's plenty because it means you no longer have to maintain your own NSLock for simple synchronization:

  NSMutableArray *sharedQueue = [[NSMutableArray alloc] initWithCapacity:5];
  
  ...
  
  @synchronized (sharedQueue) {
    // add an element to the shared queue
    [sharedQueue addObject:object];
  }
  
  ...
  
  @synchronized (sharedQueue) {
    // remove an element from the shared queue
    if ([sharedQueue count] > 0) {
      element = [sharedQueue removeObjectAtIndex:0];
    }
  }
See? Much more straightforward than managing a separate NSLock. (Of course, for shared work queues you're generally better off using an NSConditionLock but this is just an illustration...)

Hopefully Objective-C will continue to evolve and do so in the right direction. There are some interesting discussions taking place now about the language's future in various places like the GNUstep discussion list and the comp.lang.objective-c Usenet newsgroup.

Cocoa: Key-Value Coding FYI
userpic
[info]chanson
Here's an FYI for anyone using Key-Value Coding in Cocoa: If you have an attribute that has a C type of long long or unsigned long long and you try to access it using KVC, an unknown key exception will be raised. Evidently KVC doesn't know it can convert 64-bit integer types to instances of NSNumber.

Update: This is addressed in either Panther or Tiger, I can't remember which.