There are a few things to consider. First, you should arguably make efforts to not re-dispatch from the main thread to the main thread, so your -method3
should arguably look like:
- (void)method3
{
dispatch_block_t work = ^{
// The actual work that method3 does
};
if ([NSThread isMainThread])
work();
else
dispatch_async(dispatch_get_main_queue(), work);
}
I have a couple of "adapter" functions that I sometimes use for this. Here they are:
// If we're already on a background thread, just do it now, otherwise dispatch_async
void ensure_bg_thread_trysync(dispatch_block_t block)
{
if (![NSThread isMainThread])
{
block();
}
else
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
}
// If we're already on the main thread, just do it now, otherwise dispatch_async
void ensure_main_thread_trysync(dispatch_block_t block)
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_async(dispatch_get_main_queue(), block);
}
}
As for chaining your operations one after the other, there are a number of options. Continuation Passing Style is a little clunky in Objective-C, but can be reasonably usable. The idea here is that your methods return void
and take a block parameter of "what to do next." So your example, with -method3
being adapted to use CPS might look like (using the above functions for convenience:
- (void)method3AndThen: (dispatch_block_t)continuation
{
ensure_main_thread_trysync(^{
// The actual work that method3 does
// ...
// Then call the next thing...
if (continuation) continuation();
};
}
dispatch_async(queue, ^{
[self method1];
[self method2];
[self method3AndThen: ^{
ensure_main_thread_trysync(^{
// Update UI
});
}];
});
The other option, that's more Objective-C-ish would be to use NSOperationQueue
and NSOperation
, which have built-in support for dependency chaining across queues. Your example might look like this using NSOperation
s:
NSOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{ [self method1]; }];
NSOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{ [self method2]; }];
NSOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{ [self method3]; }];
NSOperation* updateUIOp = [NSBlockOperation blockOperationWithBlock:^{
NSParameterAssert([NSThread isMainThread]);
NSLog(@"Updating UI on Main Thread");
}];
[updateUIOp addDependency: op1];
[updateUIOp addDependency: op2];
[updateUIOp addDependency: op3];
NSOperationQueue* bgQueue = [NSOperationQueue new];
[bgQueue addOperation: op1]; // Background Thread
[bgQueue addOperation: op2]; // Background Thread
[[NSOperationQueue mainQueue] addOperation: op3]; // Main thread
[[NSOperationQueue mainQueue] addOperation: updateUIOp]; // Main thread
Note: This code assumes it's OK for -method1
, -method2
, and -method3
to execute concurrently with one another. If they needed to execute sequentially, you would just add dependencies between them, like this: [op2 addDependency: op1]; [op3 addDependency: op2];
GCD is the new hotness, and so it tends to get more air time these days, but NSOperation
/NSOperationQueue
is pretty powerful (not to mention that they're implemented on top of GCD)