views:

347

answers:

2

My main program spawns a thread, which executes the following:

// alloc autorelease pool somewhere before
NSArray *blah = [NSArray arrayWithObject: @"moo"];
[self performSelectorOnMainThread: @selector(boonk:) withObject: blah
      waitUntilDone: NO];
// release autorelease pool somewhere after

Now, this seems buggy to me because the autorelease pool could be released before the selector boonk: is finished executing, which would cause a crash.

So, my natural next move would be:

// alloc autorelease pool somewhere before
NSArray *blah = [[NSArray alloc] initWithObject: @"moo"];
[self performSelectorOnMainThread: @selector(boonk:) withObject: blah
      waitUntilDone: NO];
// release autorelease pool somewhere after


- (void)boonk: (id)data
{
   // do something with data
   [data release];   // release the ref count the thread added
}

This definitely is bug-free, but .... seems unnatural. Is there an objective-c ref counting convention or protocol to handle this situation (cross thread NO-wait posting), or is the second solution above the way it's done?

+4  A: 

Actually, performSelectorOnMainThread retains its arguments until after the selector is performed, so there's no need to do it.

Adam Woś
Well, I'll be danged, that's it. They hid that in a tiny little note at the end of the method description. Fantastic, thanks!
Well, I only remembered the *until selector is performed*, not *until after* :)
Adam Woś
Hmmm, wouldn't the argument (NSArray blah) be released *after* performing the selector? The whole purpose of retaining the argument by the framework is to free *you* from retaining and releasing it and to ensure you don't forget to retain it causing a crash. Autorelease pools are out of scope here because the coder does not retain the argument, but the framework both retains and releases it when necessary.
Costique
@Costique, please see the revised answer - I did not remember the *after* part, hence my previous version of the answer :)
Adam Woś
+1  A: 

The rule is simple; to pass an object from thread A to thread B, there must exist a hard retain. Now, as documented, -performSelectorOnMainThread: (and variants) do retain the objects until the method is finished executing, regardless of synchronous or asynchronous invocation.

However, it is generally sensible to maintain a retain-on-sending-thread-that-is-released-on-receiving-thread motif. It is explicit in intention and will support future refactorings, potentially to other models that do not do the automatic retain/release.

And, to repeat because it is important, autorelease pools cannot be used to preserve objects across threads.

bbum