Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread? [duplicate]
Asked Answered
M

1

14

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...

Mischance answered 17/7, 2012 at 20:4 Comment(2)
closely related: What's the difference between performSelectorOnMainThread: and dispatch_async() on main queue?Weaponless
The most obvious difference from the POV of writing the code is that dispatching a block is more flexible: it's way easier to handle primitive arguments, for example.Weaponless
C
14

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.
Clamber answered 17/7, 2012 at 23:2 Comment(2)
1. performSelectorOnMainThread now shows an undeclared-selector warning.Langsyne
If you turn on the Undeclared Selector warning in Xcode build settings then you do get some build time testing anytime @selector is used, including in performSelectorOnMainThread. This helps with typos, but be aware of this warning's limitations. It checks if any matching selector has been declared yet, not just those for the object you are calling performSelectorOnMainThread on. Second, this enforces a coding style convention that methods and selectors must be declared before being used.Clamber

© 2022 - 2024 — McMap. All rights reserved.