Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?

ElasticThoughts picture ElasticThoughts · Jul 17, 2012 · Viewed 27.7k times · Source

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

torrey.lyons picture torrey.lyons · Jul 18, 2012

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.