Perform UI Changes on Main Thread Using Dispatch_Async or Performselectoronmainthread

Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?

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.

What's the difference between performSelectorOnMainThread: and dispatch_async() on main queue?

By default, -performSelectorOnMainThread:withObject:waitUntilDone: only schedules the selector to run in the default run loop mode. If the run loop is in another mode (e.g. the tracking mode), it won't run until the run loop switches back to the default mode. You can get around this with the variant -performSelectorOnMainThread:withObject:waitUntilDone:modes: (by passing all the modes you want it to run in).

On the other hand, dispatch_async(dispatch_get_main_queue(), ^{ ... }) will run the block as soon as the main run loop returns control flow back to the event loop. It doesn't care about modes. So if you don't want to care about modes either, dispatch_async() may be the better way to go.

iOS Threads and making UI changes on the main thread

You can use GCD to make it multithreaded, and give signal when indicator start and stop.
performSelector... will be executed when function is finished..
and It doesn't say how you call the AcceptCallBack method, I guess it's already on main thread. If it is then you need to call the AcceptCallback in another thread and use below code to perform "invoke" method on main thread.

dispatch_async(dispatch_get_main_queue(), ^{
<do work here>
});

http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

EDIT:

I would do it like this

static dispatch_queue_t processing_queue;
static dispatch_queue_t request_processing_queue() {
if (processing_queue == NULL) {
processing_queue = dispatch_queue_create("com.xxx.processing", 0);
}
return processing_queue;
}

void AcceptCallBack(
CFSocketRef socket,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{

__block MasterViewController* mvc = (__bridge MasterViewController*)info;

dispatch_async(processing_queue(), ^{

dispatch_async(dispatch_get_main_queue(), ^{
[mvc invoke];
});

..... < Do AcceptCallback Code Here >

dispatch_async(dispatch_get_main_queue(), ^{
[mvc hide];
});

});

}

WARNING: This is just pseudo code..

Objective C- Trouble updating UI on main thread

dispatch_async(dispatch_get_global_queue(0, 0), ^{
//load your data here.
dispatch_async(dispatch_get_main_queue(), ^{
//update UI in main thread.
});
});

IOS: Main thread slowing down during background operation

You just did something redundant. You dispatched the fetching of data in a background thread. But then you also did the [self.tableView reloadData]; in the background thread. That's why your UI will be affected.

Try this:

if(hasInternet==YES && [loggedIntoServer isEqual:@1]) 
{
dispatch_async(kBgQueue, ^
{
NSData* data = [NSData dataWithContentsOfURL: kProductsURL];

if (data)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self fetchData:data];
});
}
});
}

What I did is I changed this part of your code:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self fetchData:data];
});

Because you should only do any changes to UI in the main thread. and this part of my code is doing the job in main thread.

dispatch_async(dispatch_get_main_queue(), ^
{
[self fetchData:data];
});

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

performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.

Here's how the documentation says the method is implemented:

- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}

And on performSelector:target:withObject:order:modes:, the documentation states:

This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.

Calling a method on the main thread?

Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});

Swift

DispatchQueue.main.async {
self.doSomething()
}

Legacy Swift

dispatch_async(dispatch_get_main_queue()) {
self.doSomething()
}

GCD vs performSelectorInBackground/performSelectorOnMainThread

When to use performSelectorInBackground:

Never. Do not use this method. It spawns an unbounded number of threads. Even before GCD was available, this was a horrible method.

When to use performSelectorOnMainThread:

Meh… Never, but just because it's inconvenient. There's nothing deeply wrong with this method. It's just not as useful as dispatch_async().

The difference between GCD and the old performSelector… methods (and NSThread in general) is that GCD manages a thread pool for you. In general, you should avoid manual threading in Cocoa. Instead, use NSOperationQueue or GCD (dispatch methods). They provide a more useful queue abstraction rather than forcing you to manually manage threads.

Be sure to read over Apple's Migrating Away from Threads for more info.

What is the difference between GCD main queue and the main thread?

Yes, there's a difference. The main dispatch queue is a serial queue. That means that, while it's running a task that's been submitted to it, it can't run any other tasks. That's true even if it runs an inner event loop.

-performSelectorOnMainThread:... operates through a run loop source. Run loop sources can fire in an inner run loop even if that inner run loop is a result of a previous firing of that same source.

One case where this bit me is with running a modal file open dialog. (Non-sandboxed, so the dialog is in-process.) I initiated the modal dialog from a task submitted to the main dispatch queue. It turns out that the internal implementation of the open dialog also dispatches some work to the main queue asynchronously. Since the main dispatch queue was occupied by my task which was running the dialog, it didn't process the framework's tasks until after the dialog completed. The symptom was that the dialog would fail to show the files until some internal timeout had expired, which was on the order of a minute or so.

Note that this wasn't a case of deadlock caused by a synchronous request to the main queue from the main thread, although that can also happen. With GCD, such a synchronous request is certain to deadlock. With -performSelectorOnMainThread:..., it won't because a synchronous request (waitUntilDone set to YES) is just run directly.

By the way, you say "the first code is asynchronous" as if to contrast with the second code. Both are asynchronous, since you passed NO for waitUntilDone in the second.


Update:

Consider code like this:

dispatch_async(dispatch_get_main_queue(), ^{
printf("outer task, milestone 1\n");
dispatch_async(dispatch_get_main_queue(), ^{
printf("inner task\n");
});
// Although running the run loop directly like this is uncommon, this simulates what
// happens if you do something like run a modal dialog or call -[NSTask waitUntilExit].
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("outer task, milestone 2\n");
});

This will log:

outer task, milestone 1
outer task, milestone 2
inner task

The inner task won't get to run until the outer task has completed. That's true even though the outer task ran the main run loop, which is what processes tasks dispatched to the main queue. The reason is that the main queue is a serial queue and will never start a new task while it's still running a task.

If you change the inner dispatch_async() to dispatch_sync(), then the program will deadlock.

By contrast, consider:

- (void) task2
{
printf("task2\n");
}

- (void) task1
{
printf("task1 milestone 1\n");
[self performSelectorOnMainThread:@selector(task2) withObject:nil waitUntilDone:NO];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("task1 milestone 2\n");
}

(... in some other method:)
[self performSelectorOnMainThread:@selector(task1) withObject:nil waitUntilDone:NO];

That will log:

task1 milestone 1
task2
task1 milestone 2

Running the run loop inside of -task1 gives an opportunity for the inner -performSelectorOnMainThread:... to run. This is the big difference between the two techniques.

If you change the NO to YES in -task1, this still works without deadlock. That's another difference. That's because, when -performSelectorOnMainThread:... is called with waitUntilDone set as true, it checks if it's called on the main thread. If it is, then it just directly invokes the selector right there. It's as though it were just a call to -performSelector:withObject:.

How to do a long operation on background thread and then later update UI on main thread

Assuming you have a handle to the view controller where you want to update the UI, you could use the NSObject "performSelectorOnMainThread:withObject:waitUntilDone:" method.

2) you could also utilize Grand Central Dispatch with code like this:

dispatch_async(dispatch_get_main_queue(), ^{
[viewController doSomethingWhenBigOperationIsDone];
});

3)
Or you could even post an NSNotification from your background thread where the observer (in the main thread) could update the UI.

You might find some additional useful information in this related question.



Related Topics



Leave a reply



Submit