If I do this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self someMethod];
});
And someMethod is this:
-(void)someMethod{
//doing stuff with no mention of GCD
}
Will someMethod run inside the dispatch queue, or will that queue wait for someMethod to run on the main thread, since someMethod does not itself dispatch anything to other queues?
Methods are executed on the thread or queue from which they are invoked. So, if you wanted to update UI after processing data on a background queue, you'd need to explicitly execute your UI update on the main thread.
For example:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self someMethod];
});
- (void) someMethod{
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI here
});
}
Related
I have a series of dispatch_async that I am performing and I would like to only update the UI when they are all done. Problem is the method within dispatch_async calls something in a separate thread so it returns before the data is fully loaded and dispatch_group_notify is called before everything is loaded.
So I introduce a infinite loop to make it wait until a flag is set.
Is this the best way? See code below.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_async(group, queue, ^{
__block BOOL dataLoaded = NO;
[thirdPartyCodeCallWithCompletion:^{
dataLoaded = YES;
}];
// prevent infinite loop
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
queue, ^{
dataLoaded = YES;
});
// infinite loop to wait until data is loaded
while (1) {
if (dataLoaded) break;
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//update UI
});
}
You're already aware of dispatch groups. Why not just use dispatch_group_wait(), which includes support for a timeout? You can use dispatch_group_enter() and dispatch_group_leave() rather than dispatch_group_async() to make the group not done until the internal block for the third-party call with completion is finished.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[thirdPartyCodeCallWithCompletion:^{
dispatch_group_leave(group);
}];
}
}
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSECS_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
//update UI
});
The use of dispatch_group_wait() does make this code synchronous, which is bad if run on the main thread. Depending on what exactly is supposed to happen if it times out, you could use dispatch_group_notify() as you were and use dispatch_after() to just updates the UI rather than trying to pretend the block completed.
Update: I tweaked my code to make sure that "update UI" happens on the main queue, just in case this code isn't already on the main thread.
By the way, I only used dispatch_async() for the block which calls thirdPartyCodeCallWithCompletion: because your original used dispatch_group_async() and I wasn't sure that the hypothetical method was asynchronous. Most APIs which take a completion block are asynchronous, though. If that one is, then you can just invoke it directly.
Another method is to use semaphore and the dispatch_semaphore_wait:
// Create your semaphore, 0 is specifying the initial pool size
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#autoreleasepool {
// Your code goes here
}
// Release the resource and signal the semaphore
dispatch_semaphore_signal(semaphore);
});
// Wait for the above block execution, AKA Waits for (decrements) a semaphore.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// After this line you can now safely assert anything you want regarding the async operation since it is done.
Suppose I call dispatch_async() three times in order:
dispatch_async(dispatch_get_main_queue(),
^{
[self doOne];
});
// some code here
dispatch_async(dispatch_get_main_queue(),
^{
[self doTwo];
});
// more code here
dispatch_async(dispatch_get_main_queue(),
^{
[self doThree];
});
Will this always be executed like
[self doOne], [self doTwo], then [self doThree], or is the order is guaranteed?
In this case, the question probably is if the main queue is serial or concurrent.
From the documentation:
dispatch_get_main_queue
Returns the serial dispatch queue associated with the application’s
main thread.
so the main queue is a serial queue, and [self doOne], [self doTwo], [self doThree] are executed sequentially in that order.
Concurrency Programming Guide, About Dispatch Queues:
The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. [emphasis mine]
I'm doing some background processing with GCD and saving some objects with Core Data. In method [self saveData] I'm creating a NSManagedObjectContext with concurrency type NSPrivateQueueConcurrencyType to perform the Core Data operations on a background thread. I'm running all my Core Data operations within performBlock.
Now, is it necessary to call [self saveData] from main thread or can I continue in the background thread I'm in (to avoid the extra call dispatch_async(dispatch_get_main_queue(), ^{});)
Like so:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
BOOL isProcessed = [self processData];
if (isProcessed) {
// Save with Core Data
[self saveData];
}
});
Or do I need to do:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
BOOL isProcessed = [self processData];
if (isProcessed) {
dispatch_async(dispatch_get_main_queue(), ^{
// Save with Core Data
[self saveData];
});
}
});
performBlock: and performBlockAndWait: ensure that the block operations are executed on the queue specified for the context. Therefore, it does not matter on which thread performBlock: or performBlockAndWait: are called.
The extra dispatch_async(dispatch_get_main_queue(), ^{}); is therefore not necessary if [self saveData] uses performBlock: for all operations.
This is what my code looks like now and I want to call these methods in a serial manner:
-(void) methodOnBackThread // this method will run on a background thread
{
[runner runThisMethod]; // and this will run on the same background thread as well
[runner runThisOtherMethod]; // and so will this one
// but I want this one to run on the main thread :
[runner runThisMethodOnTheMainThreadUsing:thisParameter using:thisOtherParamater andUsing:thisOtherOneAsWell];
[runner runThisOtherMethod]; // this one will run on the background thread as well
// but I want this one to run on the main thread :
[runner runThisMethodOnTheMainThreadUsing:thisParameter using:thisOtherParamater andUsing:thisOtherOneAsWell];
[runner runThisOtherMethod]; // this one will run on the background thread as well
// etc..
}
I believe I have to use dispatch_get_main_queue but I can't figure out how to implement this in the above case.
How do I submit [runner runThisMethodOnTheMainThreadUsing:thisParameter using:thisOtherParamater andUsing:thisOtherOneAsWell]; to the Main Thread, then return to the execution of the rest of my background methods and then get the main thread again if the next method in line needs it?
If you are targeting iOS4 and aboveUse grand central dispatch. You can do something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//do some stuff here in the background
dispatch_async(dispatch_get_main_queue(), ^{
//do some stuff here in the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//do some stuff here in the background after finishing calling a method on the main thread
});
});
});
You can use dispatch_get_main_queue like:
dispatch_async(dispatch_get_main_queue(), ^{
if (backgroundTask != UIBackgroundTaskInvalid)
{
[runner runThisMethodOnTheMainThreadUsing:thisParameter using:thisOtherParamater andUsing:thisOtherOneAsWell];
}
});
For a better understanding about dispatch check this link
I'm trying to perform the acquisition of data from the internet on the load of my view. To not lag the UI, I'm performing the HTML download and parsing by using
[self performSelectorInBackground:#selector(alertThreadMethod) withObject:nil];
which checks to see if there is an alert online. In order to display the information on the view however, iOS says that I need to use the main thread. So i call the display code right after:
[self performSelectorInBackground:#selector(alertThreadMethod) withObject:nil];
[self loadAlert];
In doing this, the [self loadAlert]; actually runs before the selector in the background (it is faster). Because of this, it does not have the information that the selector in the background is supposed to provide it.
How can I ensure that [self loadAlert]; runs after? Or is there a better way to do this?
You can either move loadAlert invocation into the alertThreadMethod or use Grand Central Dispatch serial queues, e.g.,
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
[self alertThreadMethod];
[self loadAlert];
});
dispatch_release(queue);
Or, if loadAlert is updating the UI, since you do UI updates in the main queue, you'd do something like:
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
[self alertThreadMethod];
dispatch_async(dispatch_get_main_queue(), ^{
[self loadAlert];
});
});
dispatch_release(queue);
By the way, if you're just doing this one task in background, rather than creating your own serial queue, you might just use one of the existing background queues. You only need to create a queue if you need the serial nature (i.e. you're going to be numerous dispatch_async calls and you can't have them running concurrently). But in this simple case, this might be even a little more efficient, bypassing the creating and releasing of the serial queue:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self alertThreadMethod];
dispatch_async(dispatch_get_main_queue(), ^{
[self loadAlert];
});
});
In your alertThreadMethod, after you have your information, call the method performSelectorOnMainThread:withObject:waitUntilDone: and pass it a selector to your loadAlert method.
-(void)alertThreadMethod
{
// get your information here
performSelectorOnMainThread:#selector(loadAlert) withObject:nil waitUntilDone:NO
}