CocoaNoob CocoaNoob - 6 months ago 52
iOS Question

Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?


Possible Duplicate:

Grand Central Dispatch (GCD) vs. performSelector - need a better explanation




To execute "stuff" on the main thread, should I use
dispatch_async
or
performSelectorOnMainThread
? Is there a preferred way, right/or wrong, and/or best practice?

Example: I'm performing some logic within the block of an
NSURLConnection sendAsynchronousRequest:urlRequest
method. Because I'm doing stuff to the main view such as presenting a
UIAlertView
I need to show the
UIAlertView
on the main thread. To do this I'm using the following code.

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

// code snipped out to keep this question short

if(![NSThread isMainThread])
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oops!" message:@"Some Message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
});
}
}];


Within that same
if(![NSThread isMainThread])
statement I also call some custom methods. The question is, should I use the
dispatch_async
method that I'm using above or is it better to use
performSelectorOnMainThread
instead? For example, full code below:

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

// code snipped out to keep this question short

if(![NSThread isMainThread])
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oops!" message:@"Some Message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];

// call custom methods in dispatch_async?
[self hideLoginSpinner];
});

// or call them here using performSelectorOnMainThread???
[self performSelectorOnMainThread:@selector(hideLoginSpinner) withObject:nil waitUntilDone:NO];
}
}];


FYI - If I DO NOT perform these actions on he main thread I see a few second delay when presenting the
UIAlertView
and I receive the following message in the debugger
wait_fences: failed to receive reply: 10004003
. I've learned that this is because you need to make changes to the UI on the main thread... In case someone is wondering why I'm doing what I'm doing...

Answer

As mentioned in the links provided by Josh Caswell, the two are almost equivalent. The most notable differences is that performSelectorOnMainThread will only execute in the default run loop mode and will wait if the run loop is running in a tracking or other mode. However, there are some significant differences for writing and maintaining the code.

  1. dispatch_async has the big advantage that the compiler does all its usual tests. If you mistype the method in performSelectorOnMainThread you fail at run time, rather than compile time.
  2. dispatch_async makes it much easier to return data from the main thread using the __block qualifier.
  3. dispatch_async makes it much easier to handle primitive arguments since you don't have to wrap them in an object. However, this comes with a potential pitfall. If you have a pointer to some data remember that block capture does not deep copy the data. On the other hand wrapping the data in an object as you would be forced to do for performSelectorOnMainThread does deep copy (unless you set special options). Without a deep copy you can run into intermittent bugs that are frustrating to debug. So this means you should wrap things like char * in NSString before you call dispatch_async.