のコードを見ない-postImage:completionHandler:
と、どこでスケジュールされているかを判断するのは難しいですが、iOS によって提供される何かを呼び出していると思います。その場合、ブロック内のハンドラー ブロックが非同期的にグローバル キューにディスパッチされ、iOS 提供の関数またはメソッドがすぐに返されます。ディスパッチ グループに関する限り、作業はほぼ瞬時に完了します。
呼び出しが行われるまでにまだスケジュールされていない作業をグループに待機させる簡単な方法はありませんdispatch_group_wait()
。ただし、セマフォと呼ばれる低レベルの Thingamajigger を追加して、アクションが正しい順序で完了するようにし、内部 (非同期) ブロックの範囲外でスケジュールすることができます。
NSUInteger numKeys = [keys count];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSUInteger i = 0; i < numKeys; i++) {
__block NSString *key = [keys objectAtIndex:i];
dispatch_group_async(group, queue, ^{
// We create a semaphore for each block here. More on that in a moment.
// The initial count of the semaphore is 1, meaning that a signal must happen
// before a wait will return.
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
[self postImage:[images objectForKey:key] completionHandler:^(ServerResponse *response){
...
// This instructs one caller (i.e. the outer block) waiting on this semaphore to resume.
dispatch_semaphore_signal(sem);
}];
// This instructs the block to wait until signalled (i.e. at the end of the inner block.)
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
// Done with the semaphore. Nothing special here.
dispatch_release(sem);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Now all the tasks should have completed.
dispatch_release(group);
ただし、ここで問題があります。セマフォはカーネル リソースです。実行するタスクが 100 個あるのに、カーネルが 99 個のセマフォしか提供できないとしたら? Bad Things™ が発生します。1 つのセマフォのみを使用するようにコードを再構築できますが、それを待つのは少し不安定に見えます。ちなみに、これを行うと実際にはディスパッチ グループが完全に不要になるため、基本的にグループをセマフォに置き換えます。見よう!
NSUInteger numKeys = [keys count];
// set the count of the semaphore to the number of times it must be signalled before
// being exhausted. Up to `numKeys` waits will actually wait for signals this way.
// Additional waits will return immediately.
dispatch_semaphore_t sem = dispatch_semaphore_create(numKeys);
for (int i = 0; i < numKeys; i++) {
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSUInteger i = 0; i < numKeys; i++) {
__block NSString *key = [keys objectAtIndex:i];
dispatch_async(queue, ^{
[self postImage:[images objectForKey:key] completionHandler:^(ServerResponse *response){
...;
// This decrements the semaphore's count by one. The calling code will be
// woken up by this, and will then wait again until no blocks remain to wait for.
dispatch_semaphore_signal(sem);
}];
});
}
// At this point, all the work is running (or could have already completed, who knows?).
// We don't want this function to continue running until we know all of the blocks
// have run, so we wait on our semaphore a number of times equalling the number
// of signals we expect to get. If all the blocks have run to completion before we've
// waited for all of them, the additional waits will return immediately.
for (int i = 0; i < numKeys; i++) {
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
// Now all the tasks should have completed.
dispatch_release(sem);