Reputation: 4325
I have a piece of code written inside a dispatch async block. Like below:
dispatch_queue_t queue= dispatch_queue_create("action1", NULL);
dispatch_async(queue, ^{
[self method1];
[self method2];
[self method3];
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI
});
});
Now, what if I want to execute method3 on main thread? simply use
- (void) method3
{
dispatch_async(dispatch_get_main_queue(), ^{
//Do method 3 on main thread
});
}
Is this the right way to do it ?? I just want method 1 2 and 3 to be executed first before my UI is updated and method3 needs to be executed on the main thread.
Upvotes: 2
Views: 402
Reputation: 29886
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)
Upvotes: 2
Reputation: 437422
Yes, this is fine. Admittedly, it feels redundant to have method 3 dispatch something to the main queue, only to have the routine that called that third method then turn around and dispatching something else to the main queue. That might be fine (I can construct scenarios where it would be logical to do precisely that). Whether it makes sense to dispatch twice to the main thread depends entirely upon what these two blocks are performing, but without a little context, it feels a bit redundant.
But setting this aside, there's nothing wrong with code that creates a queue, dispatches code to it, but then dispatches some final bit of code back to the main thread. It's a very common pattern.
Upvotes: 2