Re: Autorelease and passing by reference in background threads
Re: Autorelease and passing by reference in background threads
- Subject: Re: Autorelease and passing by reference in background threads
- From: Greg Parker <email@hidden>
- Date: Mon, 27 Apr 2009 15:02:54 -0700
On Apr 27, 2009, at 12:17 PM, Symphonik wrote:
I am using a couple of methods that use NSError ** pointers to
communicate error conditions. These methods run on background
threads and so have their own autorelease pools set up. I pass an
NSError pointer down a couple of method calls -- by the time it
comes back up, it has been dealloc'd by the autorelease pool.
[...]
- (void)runSomethingThatWillFail:(NSError **)error {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *directoryContents = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:@"/BOGUS" error:error];
[*error retain];
[pool release];
[*error autorelease];
}
Now it works and isn't leaking anymore. But factor in that I have to
do some checking on the existence of the error before I dereference
it, etc... this is becoming some ugly code just to get my NSError
back up the chain.
Am I overthinking this? Anyone else have better suggestions?
That is the correct way to handle an autoreleased object that crosses
a pool boundary; you would need to do the same thing with an ordinary
autoreleased return value too. The best way to simplify your code and
avoid typos is to wrap the *error handling and pool release in a
single #define.
#define RELEASE_POOL_KEEP_ERROR (pool, error) \
do { \
if (error) [*error retain]; \
[pool release]; \
if (error) [*error autorelease]; \
} while (0)
I'm confused about one part of your description. You say that these
methods have autorelease pools because they run on background threads.
But either there's another autorelease pool set up outside here, in
which case you don't need the local pool after all, or [*error
autorelease] is a leak. The leak might be the kind that logs
"autorelease with no autorelease pool" to the console, or the kind
where there is a pool in place but it's immortal. Also, if you intend
to pass that error object back to some other calling thread then you
will need code to keep the object alive other than the background
thread's autorelease pool; otherwise you crash when the background
thread's autorelease pool is drained before the other thread retains
the error for itself.
There is one alternative to the retain/autorelease dance, sometimes:
don't drain your pool at all.
- (void)runSomethingThatWillFail:(NSError **)error {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *directoryContents = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:@"/BOGUS" error:error];
if (!(error && *error)) [pool release];
}
This version lets the pool live if there is an error. It works because
autorelease pools are nested: whenever any autorelease pool is
destroyed, all pools created after it are also destroyed. So as long
as there's some other pool outside this one, and that outer pool is
expected to be drained soon enough that you don't care about letting
everything in your pool live longer, then it's safe to simply drop
your pool on the floor and let it be cleaned up later.
This fallback cleanup of autorelease pools is intended for exception
handling: the thrown object is autoreleased, and intervening pools
between the throw and the catch are ignored until later. I see no
reason why it couldn't be used for NSErrors too.
http://sealiesoftware.com/blog/archive/2008/09/16/objc_explain_Exceptions_and_autorelease_pools.html
--
Greg Parker email@hidden Runtime Wrangler
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden