Grand Central Dispatch's groups were basically made to solve this problem. From Apple's documentation on the subject:
A dispatch group is a way to monitor a set of block objects for completion. (You can monitor the blocks synchronously or asynchronously depending on your needs.) Groups provide a useful synchronization mechanism for code that depends on the completion of other tasks. For more information about using groups, see Waiting on Groups of Queued Tasks.
There are two ways you can use groups to monitor groups of tasks. The first is to use an async callback, and the other is to block the current queue until all of the grouped tasks have completed. The setup is the same either way.
I'll go through a quick example to get you started (I'll answer in Swift but the same approach carries over 1-1 with Objective-C). First, define your group:
let group = dispatch_group_create()
Enter the group once per each async task you'd like to complete:
dispatch_group_enter(group)
dispatch_group_enter(group)
Run your async tasks, and when you want to mark each task as completed, call dispatch_group_leave
:
firstAsyncTask {
dispatch_group_leave(group)
}
secondAsyncTask {
dispatch_group_leave(group)
}
As mentioned above, when all tasks in the group have completed, you can either wait on the current queue (which will block the thread) or specify a block to be called asynchronously.
Wait
dispatch_group_wait(group, 30 * NSEC_PER_SEC)
This will stop executation on the current thread until either all of the group tasks have completed, or after 30s have elapsed (whichever is sooner).
If you want to remove any time limit:
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
Async
This one is a bit simpler, if only because there's not really much to it. You specify a block to call your block on as your second argument. Once all of the group's tasks are completed, this block is called:
dispatch_group_notify(group, dispatch_get_main_queue()) {
// Code goes here.
}