Beware the hidden autorelease pool
Can you tell what's wrong with this method?
- (BOOL)doSomethingWithDictionary:(NSDictionary *)dictionary error:(NSError **)error
{
__block BOOL result = YES;
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![self doSomethingWithObject:key andObject:obj error:error]) {
result = NO;
*stop = YES;
}
}];
return result;
}
If doSomethingWithObject:andObject:error: returns an error, the program will segfault when the returned error is accessed by the caller.
Why? Since the error is returned via an autoreleasing pointer, a returned error object must be retained within the current autorelease scope. It looks like we haven't done that here, but it turns out that enumerateKeysAndObjectsUsingBlock: (on OS X 10.7, at least) sets up an autorelease pool of its own. By the time our method returns, any returned error will have been deallocated.
Be careful about returning by writeback from within a block. You don't know whether the block will be called from within a new autorelease pool. Whether it is or isn't could even vary between OS releases.
This method should use fast enumeration or NSEnumerators to enumerate over the dictionary. Or, it can ensure that the returned error is immediately retained by declaring the pointer argument NSError *__strong *.
Written by Chris Devereux
Related protips
1 Response
Really bad UX design choice. 1) Going to an article breaks "Open in New Tab". 2) Not what is expected based on decades of usage. 3) No clear way to get back. 4) The full site flashes in as the page is scrolled.