group 头文件中函数的解析:
/*
OS_OBJECT_DECL(dispatch_object);
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)
*/
// 从上面可以看出 dispatch_group 是 dispatch_object 子类
DISPATCH_DECL(dispatch_group);
dispatch_group_t == DISPATCH_DECL(dispatch_group);
/*
创建一个组, 创建的组可能与 block 相关联。
这个调度组能够被用来等待他引用的 block 的完成。
- result : 一个新的组。 在失败的情况下返回 NULL
*/
dispatch_group_t
dispatch_group_create(void);
/**
调度组 在 queue 异步执行 block
*/
void
dispatch_group_async(
dispatch_group_t group, // 调度组对象
dispatch_queue_t queue, // 队列
dispatch_block_t block // block
);
/**
调度组 在 queue 异步执行 函数
*/
void
dispatch_group_async_f(
dispatch_group_t group, // 调度组对象
dispatch_queue_t queue, // 队列
void *context, // 上下文(传递给函数的)
dispatch_function_t work // 函数
);
long
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
// 组中异步执行任务全部执行完成的通知
void
dispatch_group_notify(
dispatch_group_t group, // 队列组
dispatch_queue_t queue, // 执行任务的队列
dispatch_block_t block); // 任务
// 组中异步执行的函数全部执行的通知
void
dispatch_group_notify_f(
dispatch_group_t group, // 队列组
dispatch_queue_t queue, // 执行任务的队列
void *context, // 上下文
dispatch_function_t work); // 函数
// 入组函数
void
dispatch_group_enter(dispatch_group_t group);
// 出组函数
void
dispatch_group_leave(dispatch_group_t group);
调度组在项目中的使用(调度组的基本使用)
调度组:
调度组是用来协调一个或多个任务提交到队列异步触发的。 应用程序可以使用调度组等待所有调度组中的所有任务的完成。
所有异步队列执行完毕后得到一个通知。
**场景: **
a 异步下载任务,b 异步下载任务, c 异步下载任务。
只有在 a, b, c 三个异步下载任务全部完成的情况下。才通知主线程刷新界面。亦可以通知某个队列执行其他的操作。
NSLog(@"before: %@", [NSThread currentThread]);
// 创建一个组 (获得一个新的,空的调度组)
dispatch_group_t g = dispatch_group_create();
// 将队列关联组
dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskA: %@", [NSThread currentThread]);
});
// 将队列关联组
dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskB: %@", [NSThread currentThread]);
});
// 将队列关联组
dispatch_group_async(g, dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskC: %@", [NSThread currentThread]);
});
// 组中的队列全部执行完毕后就通知调度组
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
NSLog(@"MainTask: %@", [NSThread currentThread]);
});
NSLog(@"after: %@", [NSThread currentThread]);
// 打印结果
2016-06-23 09:12:05.162 Thread-Objc[9371:984276] before: <NSThread: 0x7f8c437028a0>{number = 1, name = main}
2016-06-23 09:12:05.163 Thread-Objc[9371:984276] after: <NSThread: 0x7f8c437028a0>{number = 1, name = main}
2016-06-23 09:12:05.163 Thread-Objc[9371:984320] taskA: <NSThread: 0x7f8c43712610>{number = 2, name = (null)}
2016-06-23 09:12:05.163 Thread-Objc[9371:984328] taskC: <NSThread: 0x7f8c43500a80>{number = 4, name = (null)}
2016-06-23 09:12:05.163 Thread-Objc[9371:984323] taskB: <NSThread: 0x7f8c4376ada0>{number = 3, name = (null)}
2016-06-23 09:12:05.167 Thread-Objc[9371:984276] MainTask: <NSThread: 0x7f8c437028a0>{number = 1, name = main}
上面的代码是一个简单的使用方式。
调度组的使用的原理解析
打开终端输入
// 使用这个命令可以查看 GCD 的详细文档
$ man dispatch
// 查看和 group 相关的文档
$ man dispatch_group_create
文档示例
(官方文档的说明)
// dispatch_group_async() 函数的操作和下面是等价的:
The dispatch_group_async() convenience function behaves like so:
void
dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispat
ch_block_t block)
{
// 引用计数器加1
dispatch_retain(group);
// 入组
dispatch_group_enter(group);
// 任务异步执行
dispatch_async(queue, ^{
// 执行任务
block();
// 出组(通知组,任务执行完毕)
dispatch_group_leave(group);
// 引用计数器减1
dispatch_release(group);
});
}
示例代码改写
NSLog(@"before: %@", [NSThread currentThread]);
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskA: %@", [NSThread currentThread]);
dispatch_group_leave(g);
});
dispatch_group_enter(g);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskB: %@", [NSThread currentThread]);
dispatch_group_leave(g);
});
dispatch_group_enter(g);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"taskC: %@", [NSThread currentThread]);
dispatch_group_leave(g);
});
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
NSLog(@"MainTask: %@", [NSThread currentThread]);
});
NSLog(@"after: %@", [NSThread currentThread]);
// 打印结果
2016-06-23 09:35:52.744 Thread-Objc[9540:1004789] before: <NSThread: 0x7fdec3d04920>{number = 1, name = main}
2016-06-23 09:35:52.744 Thread-Objc[9540:1004789] after: <NSThread: 0x7fdec3d04920>{number = 1, name = main}
2016-06-23 09:35:52.745 Thread-Objc[9540:1004833] taskA: <NSThread: 0x7fdec3d11d30>{number = 2, name = (null)}
2016-06-23 09:35:52.745 Thread-Objc[9540:1004829] taskC: <NSThread: 0x7fdec3c09fa0>{number = 3, name = (null)}
2016-06-23 09:35:52.745 Thread-Objc[9540:1004822] taskB: <NSThread: 0x7fdec3d09a50>{number = 4, name = (null)}
2016-06-23 09:35:52.749 Thread-Objc[9540:1004789] MainTask: <NSThread: 0x7fdec3d04920>{number = 1, name = main}
/*
dispatch_group_enter(g); 操作都在 g 所在线程。
即当前线程, 在进行异步操作之前, dispatch_group_enter(g) 都已经全部执行完毕。
g 中已经完整的记录了 block 的个数。
追踪 NSLog(@"after: %@", [NSThread currentThread]); 的打印可以知道。
执行一次 dispatch_group_enter(g) 操作,block 在组中的记录就会 加 1 。
在子线程中执行一次 dispatch_group_leave(g); 会使 block 在组中的记录就会 减 1 。
当组中的 block 记录的值为 0 的时候,就会调用 dispatch_group_notify() 函数。
*/
理解上面的实例后,对于 GCD 中的组基本的学习已经差不多了
细节知识点说明:
dispatch_group_enter();
和 dispatch_group_leave();
必须成对使用。 这两个函数会更新 block 在组中的数量。
在这个示例中除了
dispatch_group_wait
和dispatch_group_async_f
基本都用了一遍。
dispatch_group_async_f
是函数调用的基本不用。
- **dispatch_group_wait :
** 意思是等待 group 中所有的 block 都执行完毕触发这个函数,或者等待指定的时间完成后触发这个函数。这个指定的时间内group 变为空,这个函数将返回一个 0 ,表示成功。否则就是一个非空的值。当参数的值为DISPATCH_TIME_FOREVER
的时候, 返回值总为空。
注意:- 只有在所有的任务都添加到队列 后 才建议调用
dispatch_group_wait ()
函数,如果后续还有新的 block 添加到 组中,请使用新的组。 ( 这个dispatch_group_wait () 还是必须在最后调用) - 只有在所有的任务都添加到队列 后 才建议调用
dispath_group_notify()
函数,如果后续还有新的 block 添加到 组中,请使用新的组。 ( 这个dispath_group_notify() 还是必须在最后调用)
- 只有在所有的任务都添加到队列 后 才建议调用
-
dispatch_group_wait() 和 dispath_group_notify() 的 选择:
dispatch_group_wait (DISPATCH_TIME_FOREVER) == dispath_group_notify()
AFN 中的网络请求超时就是使用dispatch_group_wait ()
实现的。 当一个时间结束后自动返回一个超时错误。