组 dispatch_group
很多时候,你发现需要将异步的 block 组合起来去完成一个给定的任务。这些任务中甚至有些是并行的。现在,如果你想要在这些任务都执行完成后运行一些代码,"groups" 可以完成这项任务。看这里的例子:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^(){
// Do something that takes a while
[self doSomeFoo];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.foo = 42;
});
});
dispatch_group_async(group, queue, ^(){
// Do something else that takes a while
[self doSomeBar];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.bar = 1;
});
});
// This block will run once everything above is done:
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
NSLog(@"foo: %d", self.foo);
NSLog(@"bar: %d", self.bar);
});
需要注意的重要事情是,所有的这些都是非阻塞的。我们从未让当前的线程一直等待直到别的任务做完。恰恰相反,我们只是简单的将多个 block 放入队列。由于代码不会阻塞,所以就不会产生死锁。
同时需要注意的是,在这个小并且简单的例子中,我们是怎么在不同的队列间进切换的。
对现有API使用 dispatchgroupt
一旦你将 groups 作为你的工具箱中的一部分,你可能会怀疑为什么大多数的异步API不把 dispatch_group_t
作为一个可选参数。这没有什么无法接受的理由,仅仅是因为自己添加这个功能太简单了,但是你还是要小心以确保自己使用 groups 的代码是成对出现的。
举例来说,我们可以给 Core Data 的 -performBlock:
API 函数添加上 groups,就像这样:
- (void)withGroup:(dispatch_group_t)group performBlock:(dispatch_block_t)block
{
if (group == NULL) {
[self performBlock:block];
} else {
dispatch_group_enter(group);
[self performBlock:^(){
block();
dispatch_group_leave(group);
}];
}
}
当 Core Data 上的一系列操作(很可能和其他的代码组合起来)完成以后,我们可以使用 dispatch_group_notify
来运行一个 block 。
很明显,我们可以给 NSURLConnection
做同样的事情:
+ (void)withGroup:(dispatch_group_t)group
sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
{
if (group == NULL) {
[self sendAsynchronousRequest:request
queue:queue
completionHandler:handler];
} else {
dispatch_group_enter(group);
[self sendAsynchronousRequest:request
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
handler(response, data, error);
dispatch_group_leave(group);
}];
}
}
为了能正常工作,你需要确保:
dispatch_group_enter()
必须要在dispatch_group_leave()
之前运行。dispatch_group_enter()
和dispatch_group_leave()
一直是成对出现的(就算有错误产生时)。