Sync dispatch on current queue - ios

I know you might find this an odd question, but I'm just learning GCD and I want to fully understand all its aspects. So here it is:
Is there ever any reason to dispatch a task SYNC on the CURRENT QUEUE?
For example:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(...);
dispatch_async(concurrentQueue, ^{
//this is work task 0
//first do something here, then suddenly:
dispatch_sync(concurrentQueue, ^{
//work task 1
});
//continue work task 0
});
I understand one thing: if instead of concurrentQueue I use a serial queue, then I get a deadlock on that serial queue, because work task 1 cannot start until the work task 0 is finished (because of the serial queue that guarantees order of execution), and in the same time work task 0 cannot continue its execution because it waits for the SYNC dispath function to return (please correct me if I'm wrong, that would make me a total noob).
So coming back to the original idea, is there any difference whatsoever between the code above and the same code where instead of calling the dispatch_sync function I simply write work task 1 code directly?

No. I can't think of a reason to ever dispatch_sync on the same concurrent queue you're already on. If you do that, GCD will just immediately invoke your block, in-line, on the same thread, as if you had called it directly. (I checked.) And as you pointed out, doing that on a serial queue will deadlock you.

Assume this queue for all examples:
dispatch_queue_t queue = dispatch_queue_create(“com.somecompany.queue”, nil);
Situation 1 - OK
dispatch_async(queue, ^{
[self goDoSomethingLongAndInvolved];
dispatch_async(queue, ^{
NSLog(#"Situation 1");
});
});
Situation 2 - Not OK! Deadlock!
dispatch_sync(queue, ^{
[self goDoSomethingLongAndInvolved];
dispatch_sync(queue, ^{
NSLog(#"Situation 2”); // NOT REACHED! DEADLOCK!
});
});
Situation 3 - Not OK! Deadlock!
dispatch_async(queue, ^{
[self goDoSomethingLongAndInvolved];
dispatch_sync(queue, ^{
NSLog(#"Situation 3"); // NOT REACHED! DEADLOCK!
});
});
Situation 4 - OK
dispatch_sync(queue, ^{
[self goDoSomethingLongAndInvolved];
dispatch_async(queue, ^{
NSLog(#"Situation 4");
});
});
Basically dispatch_sync does not like to be on the inside.
Only dispatch_asyncs can go inside.

Related

Issue in execute a method after completion of another method - iOS

I have two methods as loadTopicPostsFromDB and loadTopicPosts. In the loadTopicPostsFromDB method I am updating the value of a global NSString called strLastTimeStamp which should use in the loadTopicPosts. Thus, I want to execute loadTopicPostsFromDB first and after it finished(global string updated) I want to execute loadTopicPosts method.
This is how I did it. But, currently loadTopicPosts method executes before updating the global strLastTimeStamp, so always I get a wrong strLastTimeStamp.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self performSelectorOnMainThread:#selector(loadTopicPostsFromDB) withObject:nil waitUntilDone:NO];
});
dispatch_group_notify(group, queue, ^{
NSLog(#"LoadDBCompleted");
[self loadTopicPosts];
});
How can I do this, please advice me on what is the wrong in this implementation.
performSelectorOnMainThread: is finished as soon as iOS has put the task into a queue. The selector has most likely not even started running when the call returns. And really, you shouldn't be using performSelectorOnMainThread at all - the function isn't available in Swift, for good reason. The solution is a lot easier (fix the problems yourself):
dispatch_async (dispatch_get_main_queue (), ^{
[self loadTopicsFromDB];
[self loadTopicPosts];
});
You probably want to perform loadTopicsFromDB on a background thread though.
When you are doing something using network connection I advice you to use blocks to handle the endpoint of the call.
It is pretty simple to write in this code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self loadTopicsFromDB: ^(BOOL success, NSError *error) {
[self loadTopicPosts];
}];
});

Intangible Order of Execution (dispatch_semaphore_t, dispatch_group_async) and the Use of Them in Combination with Different Dispatch Queue Types

I just took some time in the evening to play around with GCD, especially with dispatch_semaphore_t because I never used it. Never had the need to.
So I wrote the following as a test:
- (void)viewDidLoad
{
UIView *firstView = [[UIView alloc] initWithFrame:(CGRect){{0, 0}, self.view.frame.size.width/4, self.view.frame.size.width/5}];
firstView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:firstView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
for (long i = 0; i < 1000; i++)
{
sleep(5);
dispatch_async(dispatch_get_main_queue(), ^
{
firstView.layer.opacity = ((i%2) ? 0: 1);
});
}
});
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group1 = dispatch_group_create();
dispatch_group_async(group1, queue1, ^
{
sleep(3);
NSLog(#"dispatch group 1");
});
dispatch_group_notify(group1, queue1, ^
{
NSLog(#"dispatch notify 1");
});
dispatch_async(myQueue, ^
{
for(int z = 0; z < 10; z++)
{
NSLog(#"%i", z);
sleep(1);
}
dispatch_semaphore_signal(mySemaphore);
});
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Loop is Done");
}
If I ran the above, the output would be:
0
1
2
dispatch group 1
dispatch notify 1
3
4
5
6
7
8
9
Loop is Done
After the above, firstView appears on the screen (before semaphore the whole screen was black) and finally this gets executed:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
for (long i = 0; i < 1000; i++)
{
sleep(5);
dispatch_async(dispatch_get_main_queue(), ^
{
firstView.layer.opacity = ((i%2) ? 0: 1);
});
}
});
Which only alternates the opacity as the loop runs after semaphore is done.
1.)
So, it seems that I have to wait until dispatch_semaphore finish to do its work before any UI thing takes place.
BUT:
It seems like dispatch_group_t runs concurrently with dispatch_semaphore as shown from the output above (i.e., 1, 2, 3, ....).
???
2.)
And if I change the for loop in the above to using: dispatch_async(dispatch_get_main_queue(), ^
instead of:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^,
nothing gets shown on the screen even after semaphore is done.
How so???
3.)
Furthermore if I change semaphore to the following as opposed to using a global queue like in the above:
dispatch_async(dispatch_get_main_queue(), ^
{
for(int z = 0; z < 10; z++)
{
NSLog(#"%i", z);
sleep(1);
}
dispatch_semaphore_signal(mySemaphore);
});
Only dispatch_group takes place; nothing else take places / get executed, not the for loop in the above, including UI. Nothing.
4.)
So besides what I pointed out in the above, what can I do, in order to make semaphore not blocking my UI and my other process and just let my UI and the other processes do their thing?
And as mentioned above, why changing the type of queue for semaphore from global to main would cause nothing to be shown on screen and even the loop would not execute except dispatch_group?
5.)
If I change semaphore to:
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(1);
//1 instead of 0 (zero)
Everything (i.e., both for loop and UI) runs immediately and NSLog(#"Loop is Done"); is displayed also immediately, which tells me that semaphore didn't wait here:
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Loop is Done");
???
I spent the whole evening trying to figure this out, but to no avail.
I hope someone with great GCD knowledge can enlighten me on this.
First things first: As a general rule, one should never block the main queue. This rule about not blocking the main queue applies to both dispatch_semaphore_wait() and sleep() (as well as any of the synchronous dispatches, any group wait, etc.). You should never do any of these potentially blocking calls on the main queue. And if you follow this rule, your UI should never become non-responsive.
Your code sample and subsequent questions might seem to suggest a confusion between groups and semaphores. Dispatch groups are a way of keeping track of a group of dispatched blocks. But you're not taking advantage of the features of dispatch groups here, so I might suggest excising them from the discussion as it's irrelevant to the discussion about semaphores.
Dispatch semaphores are, on the other hand, simply a mechanism for one thread to send a signal to another thread that is waiting for the signal. Needless to say, the fact that you've created a semaphore and sent signals via that semaphore will not affect any of your dispatched tasks (whether to group or not) unless the code in question happens to call dispatch_semaphore_wait.
Finally, in some of your later examples you tried have the semaphore send multiple signals or changing the initial count to supplied when creating the semaphore. For each signal, you generally want a corresponding wait. If you have ten signals, you want ten waits.
So, let's illustrate semaphores in a way where your main queue (and thus the UI) will never be blocked. Here, we can send ten signals between two separate concurrently running tasks, having the latter one update the UI:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// send 10 signals from one background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(#"Sleeping %d", i);
sleep(3);
NSLog(#"Sending signal %d", i);
dispatch_semaphore_signal(semaphore);
}
NSLog(#"Done signaling");
});
// and on another thread, wait for those 10 signals ...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(#"Waiting for signal %d", i);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"Got signal %d", i);
// if you want to update your UI, then dispatch that back to the main queue
dispatch_async(dispatch_get_main_queue(), ^{
// update your UI here
});
}
NSLog(#"Done waiting");
});
This is, admittedly, not a terribly useful example of semaphores, but it illustrates how theoretically you could use them. In practice, it's rare that you have to use semaphores, as for most business problems, there are other, more elegant coding patterns. If you describe what you're trying to do, we can show you how to best achieve it.
As for an example with non-zero value passed to dispatch_semaphore_create, that's used to control access to some finite resource. In this example, let's assume that you had 100 tasks to run, but you didn't want more than 5 to run at any given time (e.g. you're using network connections (which are limited), or the each operation takes up so much memory that you want to avoid having more than five running at any given time). Then you could do something like:
// we only want five to run at any given time
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
// send this to background queue, so that when we wait, it doesn't block main queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 100; i++)
{
// wait until one of our five "slots" are available
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// when it is, dispatch code to background queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"starting %d", i);
// to simulate something slow happening in the background, we'll just sleep
sleep(5);
NSLog(#"Finishing %d", i);
// when done, signal that this "slot" is free (please note, this is done
// inside the dispatched block of code)
dispatch_semaphore_signal(semaphore);
});
}
});
Again, this isn't a great example of semaphores (in this case, I'd generally use an NSOperationQueue with a maxConcurrentOperationCount), but it illustrates an example of why you'd use a non-zero value for dispatch_source_create.
You've asked a number of questions about groups. I contend that groups are unrelated to your own semaphores. You might use a group, for example, if you want to run a block of code when all of the tasks are complete. So here is a variation of the above example, but using a dispatch_group_notify to do something when all of the other tasks in that group are complete.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // or create your own concurrent queue
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_group_t group = dispatch_group_create();
// send this to background queue, so that when we wait, it doesn't block main queue
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 100; i++)
{
// wait until one of our five "slots" are available
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// when it is, dispatch code to background queue
dispatch_group_async(group, queue, ^{
NSLog(#"starting %d", i);
// to simulate something slow happening in the background, we'll just sleep
sleep(5);
NSLog(#"Finishing %d", i);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_notify(group, queue, ^{
NSLog(#"All done");
});
});

Objective-C - Wait for call on another thread to return before continuing

In my iOS application, I have a database call that takes some time to complete. I have a spinner visible on the screen while this operation is taking place. I am hitting an error with the app crashing with "com.myapp failed to resume in time" so it seems like it is running the database call on the main thread, causing issues.
Current Code
-(void)timeToDoWork
{
...
[CATransaction flush];
[[DatabaseWorker staticInstance] doWork];
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
To get the doWork function to take place on a background thread, it looks like I can use Grand Central Dispatch:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[[DatabaseWorker staticInstance] doWork];
});
However, how do I prevent the execution from continuing until it is complete? Should I end the method after the doWork call, and move everything below it to a new function?
Sample
-(void)timeToDoWork
{
...
[CATransaction flush];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[[DatabaseWorker staticInstance] doWork];
dispatch_async(dispatch_get_main_queue(), ^{
[self doneDoingWork];
});
});
}
-(void)doneDoingWork
{
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
Is there a better way to do this?
Prevent execution in main thread from continuing is really bad idea. iOS will terminate your application since main thread should always work with run loop.
I suggest you following way to handle your problem:
Write a "Locker". Let it show some view with animated spinner and no buttons at all.
When you start dispatch async operation just bring it to the front and let it work with run loop.
When your async operation completes close the locker.
You can also use blocks.
e.g..
- (void)doWorkWithCompletionHandler:(void(^)())handler {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// do your db stuff here...
dispatch_async(dispatch_get_main_queue(), ^{
handler();
});
});
}
And then use it like that:
[[DatabaseWorker staticInstance] doWorkWithCompletionHandler:^{
// update your UI here, after the db operation is completed.
}];
P.S.
It might be a good idea to copy the handler block.
The error you are receiving suggests that you are doing something in application:didFinishLaunchingWithOptions: or applicationDidBecomeAction: or somewhere else in the launch cycle that is taking too long and the app is getting terminated by the launch watchdog timer. Above all, it is vital that you return as quickly as possible from these methods. I'm not sure where your code fits into the launch cycle; but this explanation seems plausible.
There are all sorts of ways to address this; but taking the lengthy process off the main queue is the first step as you noted. Without knowing more about what main queue objects (e.g. UI) depend on this database transaction, I'd say that your suggested solution is perfectly fine. That is, dispatch the work to a background queue; and on completion dispatch the remaining UI work to the main queue.
Delegates were suggested elsewhere as a solution. That's also workable although you still have to concern yourself with which queue the delegate methods get called on.
I think that you should use a delegate in your DatabaseWorker and the method doWork always run in background, so when the worker finish the work it tell to its delegate that the work is finished. The delegate method must be called in the main thread.
In the case that you have many objects that need to know when the DatabaseWorker finish instead to use a delegate I would use notifications.
EDIT:
In the DatabaseWorker class you need to implement the method doWork like this:
- (void) doWork{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
//Do the work.
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate finishWork];
});
});
}
And in the class that implement timeTodoWork:
-(void)timeToDoWork
{
...
[CATransaction flush];
[[DatabaseWorker staticInstance] setDelegate:self];
[[DatabaseWorker staticInstance] doWork];
}
#pragma mark DatabaseWorkerDelegate
- (void) finishWork{
//Additional UI stuff here
...
if([self->myReceiver respondsToSelector:self->myMessage])
{
[self->myReceiver performSelector:self->myMessage];
}
}
Also you can use:
[self performSelectorInBackground:#selector(doWorkInBackground) withObject:nil];
instead of:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
//Do the work.
});
And add a method:
- (void) doWorkInBackground{
//Do the work
[self.delegate performSelectorOnMainThread:#selector(finishWork) withObject:nil waitUntilDone:NO];
}

iOS multiple threads versus groups

I have a question about threading. I have a view which displays two images (Banner of the opponents). I have read about threads groups which can run together.
The way I have it now is:
- (void) setBanners{
[getBanner:#"TeamA"];
[getBanner:#"TeamB"];
}
- getBanner:(NSString *team){
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^(void){
..Goto server and get logo
}
}
So my question is, does this happen the same way as grouping threads or does team two method get called when one is finished ? With grouping it would look like this:
- setBanner{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
get logo team a
});
dispatch_group_async(group, queue, ^{
get logo team a
});
}
for that they are almost equal.. there is no difference except that the dispatch_group reuses threads implicitly since GCD has a thread pool
-- oh and obviously GCD uses blocks

Does dispatch_async(dispatch_get_main_queue(), ^{...}); wait until done?

I have a scenario in my app, where I want to do some time consuming task which consists of some data processing as well as UI update, in a method. My method looks like this,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
// UI UPDATE 1
// DATA PROCESSING 2
// UI UPDATE 2
// DATA PROCESSING 3
// UI UPDATE 3
}
As it is time consuming I wanted to do the data processing on the background thread, using,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
But as both data processing and UI updates are in the same method, I wanted to move only the UI updates in main thread using,
dispatch_async(dispatch_get_main_queue(), ^{
Finally my method looks like this,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
}
Does this really work? Is this really a good practice? What is the best way to achieve this?
P.S. All these three operations are interrelated to each other.
EDIT: Sorry guys. I have missed a line in the above code. My actual code looks like this.
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
});
}
Once again, I really apologize for the confusion.
No it doesn't wait and the way you are doing it in that sample is not good practice.
dispatch_async is always asynchronous. It's just that you are enqueueing all the UI blocks to the same queue so the different blocks will run in sequence but parallel with your data processing code.
If you want the update to wait you can use dispatch_sync instead.
// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
Another approach would be to nest enqueueing the block. I wouldn't recommend it for multiple levels though.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
});
});
});
});
If you need the UI updated to wait then you should use the synchronous versions. It's quite okay to have a background thread wait for the main thread. UI updates should be very quick.
You have to put your main queue dispatching in the block that runs the computation. For example (here I create a dispatch queue and don't use a global one):
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
Of course, if you create a queue don't forget to dispatch_release if you're targeting an iOS version before 6.0.
Your proposed doCalculationsAndUpdateUIs does data processing and dispatches UI updates to the main queue. I presume that you have dispatched doCalculationsAndUpdateUIs to a background queue when you first called it.
While technically fine, that's a little fragile, contingent upon your remembering to dispatch it to the background every time you call it: I would, instead, suggest that you do your dispatch to the background and dispatch back to the main queue from within the same method, as it makes the logic unambiguous and more robust, etc.
Thus it might look like:
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 1
});
/* I expect the control to come here after UI UPDATION 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 2
});
/* I expect the control to come here after UI UPDATION 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 3
});
});
}
In terms of whether you dispatch your UI updates asynchronously with dispatch_async (where the background process will not wait for the UI update) or synchronously with dispatch_sync (where it will wait for the UI update), the question is why would you want to do it synchronously: Do you really want to slow down the background process as it waits for the UI update, or would you like the background process to carry on while the UI update takes place.
Generally you would dispatch the UI update asynchronously with dispatch_async as you've used in your original question. Yes, there certainly are special circumstances where you need to dispatch code synchronously (e.g. you're synchronizing the updates to some class property by performing all updates to it on the main queue), but more often than not, you just dispatch the UI update asynchronously and carry on. Dispatching code synchronously can cause problems (e.g. deadlocks) if done sloppily, so my general counsel is that you should probably only dispatch UI updates synchronously if there is some compelling need to do so, otherwise you should design your solution so you can dispatch them asynchronously.
In answer to your question as to whether this is the "best way to achieve this", it's hard for us to say without knowing more about the business problem being solved. For example, if you might be calling this doCalculationsAndUpdateUIs multiple times, I might be inclined to use my own serial queue rather than a concurrent global queue, in order to ensure that these don't step over each other. Or if you might need the ability to cancel this doCalculationsAndUpdateUIs when the user dismisses the scene or calls the method again, then I might be inclined to use a operation queue which offers cancelation capabilities. It depends entirely upon what you're trying to achieve.
But, in general, the pattern of asynchronously dispatching a complicated task to a background queue and then asynchronously dispatching the UI update back to the main queue is very common.
No, it won't wait.
You could use performSelectorOnMainThread:withObject:waitUntilDone:.
If you want to run a single independent queued operation and you’re not concerned with other concurrent operations, you can use the global concurrent queue:
dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
This will return a concurrent queue with the given priority as outlined in the documentation:
DISPATCH_QUEUE_PRIORITY_HIGH Items dispatched to the queue will run at high priority, i.e. the queue will be scheduled for execution before any default priority or low priority queue.
DISPATCH_QUEUE_PRIORITY_DEFAULT Items dispatched to the queue will run at the default priority, i.e. the queue will be scheduled for execution after all high priority queues have been scheduled, but before any low priority queues have been scheduled.
DISPATCH_QUEUE_PRIORITY_LOW Items dispatched to the queue will run at low priority, i.e. the queue will be scheduled for execution after all default priority and high priority queues have been scheduled.
DISPATCH_QUEUE_PRIORITY_BACKGROUND Items dispatched to the queue will run at background priority, i.e. the queue will be scheduled for execution after all higher priority queues have been scheduled and the system will run items on this queue on a thread with background status as per setpriority(2) (i.e. disk I/O is throttled and the thread’s scheduling priority is set to lowest value).
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
The good practice is: Dispatch Groups
dispatch_group_t imageGroup = dispatch_group_create();
dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image successfully uploaded to S3
dispatch_group_leave(imageGroup);
}];
dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image url updated
dispatch_group_leave(imageGroup);
}];
dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
// We get here when both tasks are completed
});
OK, there are two ways of doing that:
// GLOBAL_CONCURRENT_QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalConcurrentQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(#"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(#"Hello world chekpoint 2");
});
/* the control to come here after UI UPDATION 1 */
sleep(1);
NSLog(#"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(#"Hello world chekpoint 4");
});
/* the control to come here after UI UPDATION 2 */
sleep(1);
NSLog(#"Hello world chekpoint 5");
// DATA PROCESSING 3
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 3
sleep(1);
NSLog(#"Hello world chekpoint 6");
});
});
}
// SERIAL QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(serialQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(#"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(#"Hello world chekpoint 2");
});
sleep(1);
NSLog(#"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(#"Hello world chekpoint 4");
});
});
}

Resources