Reputation: 1891
I want code within a dispatch group to finish executing before anything else happens, essentially blocking the app from doing anything until this code is done. I can't get the dispatch group to block additional code from running, however. I've tried pretty much every suggestion here on stack, but I don't know what I'm doing want.
My function:
- (void)myFunction {
NSString *myString = @"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(@"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(@"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"3 notifying that the dispatch group is finished");
}];
NSLog(@"4 all process are complete, we are done");
}
The output I want via log statements = 1,2,3,4
The output I get via log statements = 1,4,2,3
Why is my code skipping over the dispatch group and print 4 before 2 and 3? Any advice as to what I'm doing wrong is appreciated. Thank you!
Update:
Here is my doSomething
method. My code keeps hanging on a dismiss
call.
doSomething() {
viewController.dismiss(animated: false completion: { [weak self] in
doMoreCode()
})
}
Upvotes: 2
Views: 1560
Reputation: 437592
Assuming doSomething
runs asynchronously, you could wait for the group, but it's generally better to embrace asynchronous patterns, e.g. allow myFunction
to return immediately, but supply your own completion handler to that method:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = @"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(@"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(@"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"3 notifying that the dispatch group is finished");
});
}
And use it like so:
[self myFunctionWithCompletion:^{
NSLog(@"4 all process are complete, we are done");
}];
// note, it's not done when it gets here, though, because it's asynchronous
Clearly, in the above example, the dispatch group is entirely superfluous, but I assume you were doing multiple asynchronous tasks, which is why you introduced the dispatch group. If it really was just this one asynchronous task, you'd just do:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = @"Hello world";
[self doSomething:myString completion:^{
NSLog(@"The asynchronous doSomething is done");
completion();
}];
}
Upvotes: 1
Reputation: 299345
Nothing here actually blocks. dispatch_group_notify
just says "when the group finishes, run this." The tool you meant to use was dispatch_group_wait
. If you want 1,2,3,4, then you meant this:
- (void)myFunction {
NSString *myString = @"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(@"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(@"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"3 notifying that the dispatch group is finished");
NSLog(@"4 all process are complete, we are done");
}
myFunction
of course cannot be called on the main queue in iOS (since it blocks and you must never block the main queue). And it also must not be called on the same queue that doSomething:completion:
uses for its completion handler (since that queue will be blocked at dispatch_group_wait
).
Remember that dispatch_queue_notify
just adds a block to a queue to be run sometime in the future. So it's a little unclear how you expect 3 and 4 to work (in my example I just collapsed them, but maybe you're looking for something else).
Another approach is to not block the application, and just schedule stuff to run when it's supposed to. In that case you can use the main queue. It'd look like this:
- (void)myFunction {
NSString *myString = @"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(@"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(@"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"3 notifying that the dispatch group is finished");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"4 all process are complete, we are done");
});
}
Note in both examples, I'm logging 2 before calling dispatch_group_leave
. In this example, I'm also registering two things to be run on the main queue (in order) after the group is done. In this case, myFunction
will return immediately (so it can be run on the main queue), but everything should print out in order.
Upvotes: 3