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.
Related
Can anybody see a reason why this code would work fine to update UI:
__block NSDictionary *result = nil;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[SomeService sharedInstance] doSomethingGreatWithReplyBlock:^(NSDictionary * response) {
result = response;
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDisplay:result];
});
But this one won't?
__block NSDictionary *result = nil;
[[SomeService sharedInstance] doSomethingGreatWithReplyBlock:^(NSDictionary * response) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDisplay:response];
});
}];
Isn't this exactly the same? In the first example I'm waiting for the async operation to finish using a semaphore. Then dispatch_async on the main queue.
In the second one I'm calling dispatch_async (also on the main queue) directly from within the other block (which runs on some background queue). This one still calls the updateDisplay method fine - however it doesn't actually update the UI. It feels like some main thread update issue however [NSThread isMainThread] still returns true...
Is there any obvious difference I'm missing here? I'm pretty lost here and would appreciate any explanation. I have never observed such weird behavior before.
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.
I have a method that I add to a GCD queue that I have created (so it's a serial queue) and then run it async. From within that block of code I make a dispatch to the main queue, when that block of code dispatched to the main queue is complete I set a BOOL flag to YES, so that I further down in my code can check if this condition is YES then I can continue to the next method. Here is the code in short:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_sync(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
// Process is complete
processComplete = YES;
}];
});
});
while (!processComplete) {
NSLog(#"Waiting");
}
NSLog(#"Ready for next step");
However this does not work, because dispatch_sync is never able to run the code on the main queue. Is this because I'm running a while loop on the main queue (rendering it busy)?
However if I change the implementation of the while loop to this:
while (!processComplete) {
NSLog(#"Waiting")
NSDate *date = [NSDate distantFuture];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
}
It works without a glitch. Is this an acceptable solution for this scenario? Can I do it any other preferred way? What kind of magic stuff does NSRunLoop do? I need to understand this better.
Part of the main thread's NSRunLoop job is to run any blocks queued on the main thread. By spinning in a while-loop, you're preventing the runloop from progressing, so the queued blocks are never run unless you explicitly make the loop run yourself.
Runloops are a fundemental part of Cocoa, and the documentation is pretty good, so I'd reccommend reading it.
As a rule, I'd avoid manually invoking the runloop as you're doing. You'll waste memory and make make things complicated very quickly if you have multiple manual invocations running on top of one another.
However, there is a much better way of doing this. Split your method into a -process and a -didProcess method. Start the async operation with your -process method, and when it completes, call -didProcess from the completion block. If you need to pass variables from one method to the other, you can pass them as arguments to your -didProcess method.
Eg:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_sync(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
[self didProcess];
}];
});
});
You might also consider making your singleton own the dispatch queue and make it responsible for handling the dispatch_async stuff, as it'll save on all those nasty embedded blocks if you're always using it asynchronously.
Eg:
[[Singleton sharedInstance] processAyncWithCompletionBlock:^{
NSLog(#"Ready for next step...");
[self didProcess];
}];
Doing something like what you posted will most likely freeze the UI. Rather than freezing up everything, call your "next step" code in a completion block.
Example:
dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
Singleton *s = [Singleton sharedInstance];
dispatch_async(dispatch_get_main_queue(), ^{
[s processWithCompletionBlock:^{
// Next step code
}];
});
});
Don't go creating a loop like that waiting for a value inside a block, variables in blocks are read only, instead call your completion code from inside the block.
dispatch_async(queue, ^{
Singleton *s = [Singelton sharedInstance];
[s processWithCompletionBlock:^{
//process is complete
dispatch_sync(dispatch_get_main_queue(), ^{
//do something on main queue....
NSLog(#"Ready for next step");
});
}];
});
NSLog(#"waiting");
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
});
}
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
}