GCD系列是阅读官方文件和在实践中总结的一些常见的GCD用法,基本涉及全部的GCD内容形成的集合文章,文章重点在与精简和全面覆盖。
学习本集合你可以了解:
1. GCD是如何做到多线程调度的
2. 对比其他iOS的多线程方案,GCD的优势和劣势
3. 如何使用GCD相关的API我将在后续不断补充详细内容和实际案例, 欢迎关注,提问和讨论
03-dispatch_after/dispatch_time-延迟调度和操作
04-dispatch_barrier_sync/async-线程阻塞
为什么要学dispatch_block?
在向队列中添加任务时,可以直接在对应的函数中添加 block。但是如果想对任务进行操作,比如监听任务、取消任务,就需要获取对应的 block。
dispatch_block_flags
DISPATCH_ENUM(dispatch_block_flags, unsigned long,
DISPATCH_BLOCK_BARRIER,
DISPATCH_BLOCK_DETACHED,
DISPATCH_BLOCK_ASSIGN_CURRENT,
DISPATCH_BLOCK_NO_QOS_CLASS,
DISPATCH_BLOCK_INHERIT_QOS_CLASS,
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
);
dispatch_block_create创建
# 在该函数中,flags 参数用来设置 block 的标记,block 参数用来设置具体的任务。
# flags 的类型为 dispatch_block_flags_t 的枚举,用于设置 block 的标记,定义如下
dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
# 相比于 dispatch_block_create 函数,这种方式在创建 block 的同时可以指定了相应的优先级。
# dispatch_qos_class_t 是 qos_class_t 的别名
dispatch_block_t
dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
dispatch_qos_class_t qos_class, int relative_priority,
dispatch_block_t block);
qos_class_t 是一种枚举,有以下类型:
QOS_CLASS_USER_INTERACTIVE:user interactive 等级表示任务需要被立即执行,用来在响应事件之后更新 UI,来提供好的用户体验。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED:user initiated 等级表示任务由 UI 发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_DEFAULT:default 默认优先级
QOS_CLASS_UTILITY:utility 等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND:background 等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
QOS_CLASS_UNSPECIFIED:unspecified 未指明
demo
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"normal do some thing..."); });
dispatch_async(concurrentQuene, block);
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{
NSLog(@"qos do some thing...");
});
dispatch_async(concurrentQuene, qosBlock);
监听 block 执行结束
有时我们需要等待特定的 block 执行完成之后,再去执行其他任务。有两种方法可以获取到指定 block 执行结束的时机。
dispatch_block_wait
long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);
#======================#======================#======================
# 如何使用
## 因为 dispatch_block_wait 会阻塞当前线程,所以不应该放在主线程中调用。创建一个新的并队列执行
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQuene, ^{
dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"开始执行");
[NSThread sleepForTimeInterval:3]; NSLog(@"结束执行");
});
dispatch_async(allTasksQueue, block);
// 等待时长,10s 之后超时
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
long resutl = dispatch_block_wait(block, timeout);
if (resutl == 0) {
NSLog(@"执行成功");
} else {
NSLog(@"执行超时");
}
});
dispatch_block_notify
第一个Block是操作的Block, 第二个Block是完成操作之后的Block
dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue,
dispatch_block_t notification_block);
#======================#======================#======================
NSLog(@"---- 开始设置任务 ----");
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL);
// 耗时任务
dispatch_block_t taskBlock = dispatch_block_create(0, ^{
NSLog(@"开始耗时任务");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"完成耗时任务");
});
dispatch_async(serialQueue, taskBlock);
// 更新 UI
dispatch_block_t refreshUI = dispatch_block_create(0, ^{ NSLog(@"更新 UI"); });
// 设置监听
dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI);
NSLog(@"---- 完成设置任务 ----");
GCD的取消:dispatch_block_cancel
iOS8 后 GCD 支持对 dispatch block 的取消。
这个函数用异步的方式取消指定的 block。
取消操作使将来执行 dispatch block 立即返回,但是对已经在执行的 dispatch block 没有任何影响。
当一个 block 被取消时,它会立即释放捕获的资源。
如果要在一个 block 中对某些对象进行释放操作,在取消这个 block 的时候,需要确保内存不会泄漏。
# 取消当前已获取资源但尚未执行的Block
void dispatch_block_cancel(dispatch_block_t block);
# zero if not canceled. 下面的方法用来测试Block是否成功的取消了
long dispatch_block_testcancel(dispatch_block_t block);
#======================#======================#======================
dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL);
// 耗时任务
dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{
NSLog(@"开始第一个任务");
[NSThread sleepForTimeInterval:1.5f];
NSLog(@"结束第一个任务");
});
// 耗时任务
dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{
NSLog(@"开始第二个任务");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"结束第二个任务");
});
dispatch_async(serialQueue, firstTaskBlock);
dispatch_async(serialQueue, secTaskBlock);
// 等待 1s,让第一个任务开始运行
[NSThread sleepForTimeInterval:1];
dispatch_block_cancel(firstTaskBlock);
NSLog(@"尝试过取消第一个任务");
dispatch_block_cancel(secTaskBlock);
NSLog(@"尝试过取消第二个任务");
开始第一个任务
尝试过取消第一个任务
尝试过取消第二个任务
结束第一个任务
可以发现第二个任务可以顺利取消