欢迎阅读iOS探索系列(按序阅读食用效果更加)程序员
GCD全称是Grand Central Dispatch
,它是纯 C 语言,而且提供了很是多强大的函数面试
GCD的优点:安全
咱们要关注的点就是:GCD的核心——将任务添加到队列,而且指定执行任务的函数bash
dispatch_block_t block = ^{
NSLog(@"GCD的基本使用");
};
dispatch_queue_t queue = dispatch_queue_create("com.Felix", NULL);
dispatch_async(queue, block);
复制代码
这串代码最能体现GCD的核心:网络
dispatch_block_t
使用block封装任务dispatch_queue_t
建立队列dispatch_async
将任务添加到队列上述代码一般也写成这种形式多线程
dispatch_queue_t queue = dispatch_queue_create("com.Felix", NULL);
dispatch_async(queue, ^{
NSLog(@"GCD的基本使用");
});
复制代码
多线程执行任务分为dispatch_sync
同步执行任务和dispatch_async
异步执行:并发
dispatch_sync
同步执行
dispatch_async
异步执行
串行队列
(
Serial Dispatch Queue)和
并发队列(
Concurrent Dispatch Queue
):
串行队列
:线程执行只能依次逐一前后有序的执行,等待上一个执行完再执行下一个
dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL)
建立串行队列dispatch_queue_create("xxx", NULL)
建立串行队列(GCD底层会讲到)主队列
:绑定主线程,全部任务都在主线程中执行、通过特殊处理的串行的队列
dispatch_get_main_queue()
获取主队列并发队列
:线程能够同时一块儿执行,不须要等待上一个执行完就能执行下一个任务
dispatch_queue_create("xxx", DISPATCH_QUEUE_CONCURRENT);
建立并发队列全局队列
:系统提供的并发队列
dispatch_get_global_queue(0, 0)
获取系统提供的并发队列DISPATCH_QUEUE_PRIORITY_DEFAULT
=0DISPATCH_QUEUE_PRIORITY_HIGH
、DISPATCH_QUEUE_PRIORITY_DEFAULT
、DISPATCH_QUEUE_PRIORITY_LOW
、DISPATCH_QUEUE_PRIORITY_BACKGROUND
主队列和全局队列单独考虑,组合结果以总结表格为准 app
任务一个接一个执行,不开辟线程异步
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"串行&同步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// 串行&同步线程0-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// 串行&同步线程1-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// ...按顺序输出
--------------------输出结果:-------------------
复制代码
任务一个接一个执行,会开辟线程socket
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"串行&异步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// 串行&异步线程0-<NSThread: 0x6000009b8880>{number = 6, name = (null)}
// 串行&异步线程1-<NSThread: 0x6000009b8880>{number = 6, name = (null)}
// ...按顺序输出
--------------------输出结果:-------------------
复制代码
任务一个接一个执行,不开辟线程
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"并发&同步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// 串行&同步线程0-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// 串行&同步线程1-<NSThread: 0x600003b64fc0>{number = 1, name = main}
// ...按顺序输出
--------------------输出结果:-------------------
复制代码
任务乱序执行,开辟线程
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"并发&异步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600002a9cd40>{number = 1, name = main}
// 并发&异步线程1-<NSThread: 0x600002a9ca40>{number = 5, name = (null)}
// 并发&异步线程0-<NSThread: 0x600002add3c0>{number = 4, name = (null)}
// ...乱序输出
--------------------输出结果:-------------------
复制代码
下面来看一下主队列
和全局队列
的使用状况:
相互等待,形成死锁
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"主队列&同步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600001980d40>{number = 1, name = main}
// 崩溃...
--------------------输出结果:-------------------
复制代码
任务一个接一个执行,不开辟线程
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"主队列&异步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600001980d40>{number = 1, name = main}
// 主队列&异步线程0-<NSThread: 0x600001980d40>{number = 1, name = main}
// 主队列&异步线程1-<NSThread: 0x600001980d40>{number = 1, name = main}
// ...按顺序输出
--------------------输出结果:-------------------
复制代码
任务一个接一个执行,不开辟线程(同并发+同步)
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"全局队列&同步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600001980d40>{number = 1, name = main}
// 全局队列&同步线程0-<NSThread: 0x60000099c080>{number = 1, name = main}
// 全局队列&同步线程1-<NSThread: 0x60000099c080>{number = 1, name = main}
// ...按顺序输出
--------------------输出结果:-------------------
复制代码
任务乱序执行,开辟线程(同并发+异步)
- (void)test {
NSLog(@"主线程-%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"全局队列&异步线程%d-%@", i, [NSThread currentThread]);
});
}
}
--------------------输出结果:-------------------
// 主线程-<NSThread: 0x600001cd4ec0>{number = 1, name = main}
// 全局队列&异步线程2-<NSThread: 0x600001c8eb00>{number = 3, name = (null)}
// 全局队列&异步线程3-<NSThread: 0x600001c82b80>{number = 7, name = (null)}
// ...乱序输出
--------------------输出结果:-------------------
复制代码
总结一下:
执行\队列 | 串行队列 | 并发队列 | 主队列 | 全局队列 |
---|---|---|---|---|
同步执行 | 按序执行,不开辟线程 | 按序执行,不开辟线程 | 死锁 | 按序执行,不开辟线程 |
异步执行 | 按序执行,开辟线程 | 乱序执行,开辟线程 | 按序执行,不开辟线程 | 乱序执行,开辟线程 |
dispatch_after
表示在某队列中的block延迟执行
应用场景:在主队列上延迟执行一项任务,如viewDidload以后延迟1s,提示一个alertview(是延迟加入到队列,而不是延迟执行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2秒后输出");
});
复制代码
dispatch_once
保证在App运行期间,block中的代码只执行一次
应用场景:单例
、method-Swizzling
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//建立单例、method swizzled或其余任务
});
复制代码
dispatch_apply
将指定的Block追加到指定的队列中重复执行,并等到所有的处理执行结束——至关于线程安全的for循环
应用场景:用来拉取网络数据后提早算出各个控件的大小,防止绘制时计算,提升表单滑动流畅性
- (void)test {
/** param1:重复次数 param2:追加的队列 param3:执行任务 */
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
NSLog(@"dispatch_apply前");
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"dispatch_apply的线程%zu-%@", index, [NSThread currentThread]);
});
NSLog(@"dispatch_apply后");
}
--------------------输出结果:-------------------
// dispatch_apply前
// dispatch_apply的线程0-<NSThread: 0x6000019f8d40>{number = 1, name = main}
// ...是否按序输出与串行队列仍是并发队列有关
// dispatch_apply后
--------------------输出结果:-------------------
复制代码
dispatch_group_t
:调度组将任务分组执行,能监放任务组完成,并设置等待时间
应用场景:多个接口请求以后刷新页面
dispatch_group_notify
在dispatch_group_async
执行结束以后会受到通知
- (void)test {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
NSLog(@"请求一完成");
});
dispatch_group_async(group, queue, ^{
NSLog(@"请求二完成");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新页面");
});
}
--------------------输出结果:-------------------
// 请求二完成
// 请求一完成
// 刷新页面
--------------------输出结果:-------------------
复制代码
dispatch_group_enter
和dispatch_group_leave
成对出现,使进出组的逻辑更加清晰
- (void)test {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"请求一完成");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"请求二完成");
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新页面");
});
}
--------------------输出结果:-------------------
// 请求二完成
// 请求一完成
// 刷新页面
--------------------输出结果:-------------------
复制代码
调度组要注意搭配使用,必须先进组再出组,缺一不可
dispatch_group_wait
使用long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)
DISPATCH_TIME_NOW
意味着不等待直接断定调度组是否执行完毕DISPATCH_TIME_FOREVER
则会阻塞当前调度组,直到调度组执行完毕long
类型
将上述调度组代码进行改写
- (void)test {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"请求一完成");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"请求二完成");
dispatch_group_leave(group);
});
long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW);
// long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
NSLog(@"timeout=%ld", timeout);
if (timeout == 0) {
NSLog(@"按时完成任务");
} else {
NSLog(@"超时");
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"刷新页面");
});
}
--------------------输出结果:-------------------
// timeout=49
// 请求一完成
// 请求二完成
// 超时
// 刷新页面
--------------------输出结果:-------------------
复制代码
应用场景:同步锁
并发执行异步队列
会开辟线程,而任务也会由于任务复杂度和cpu的调度致使各个乱序执行完毕,好比上图中的
任务3
明明是先于
任务4
执行,可是晚于
任务4
执行完毕
此时GCD就提供了两个API——dispatch_barrier_sync
和dispatch_barrier_async
,使用这两个API就能将多个任务进行分组——等栅栏前追加到队列中的任务执行完毕后,再将栅栏后的任务追加到队列中。简而言之,就是先执行栅栏前任务
,再执行栅栏任务
,最后执行栅栏后任务
- (void)test {
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_SERIAL);
NSLog(@"开始——%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"延迟2s的任务1——%@", [NSThread currentThread]);
});
NSLog(@"第一次结束——%@", [NSThread currentThread]);
// dispatch_barrier_async(queue, ^{
// NSLog(@"----------栅栏任务----------%@", [NSThread currentThread]);
// });
// NSLog(@"栅栏结束——%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"延迟1s的任务2——%@", [NSThread currentThread]);
});
NSLog(@"第二次结束——%@", [NSThread currentThread]);
}
复制代码
不使用栅栏函数
开始——<NSThread: 0x600001068900>{number = 1, name = main}
第一次结束——<NSThread: 0x600001068900>{number = 1, name = main}
第二次结束——<NSThread: 0x600001068900>{number = 1, name = main}
延迟2s的任务1——<NSThread: 0x600001025ec0>{number = 3, name = (null)}
延迟1s的任务2——<NSThread: 0x600001025ec0>{number = 3, name = (null)}
复制代码
使用栅栏函数
开始——<NSThread: 0x6000001bcf00>{number = 1, name = main}
第一次结束——<NSThread: 0x6000001bcf00>{number = 1, name = main}
栅栏结束——<NSThread: 0x6000001bcf00>{number = 1, name = main}
第二次结束——<NSThread: 0x6000001bcf00>{number = 1, name = main}
延迟2s的任务1——<NSThread: 0x6000001fcf00>{number = 5, name = (null)}
----------栅栏任务----------<NSThread: 0x6000001bcf00>{number = 1, name = main}
延迟1s的任务2——<NSThread: 0x6000001fcf00>{number = 5, name = (null)}
复制代码
栅栏函数的做用是将队列中的任务进行分组,因此咱们只要关注任务1
、任务2
串行队列异步执行
任务是一个接一个执行完毕的,因此使用栅栏函数没意义
- (void)test {
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"开始——%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"延迟2s的任务1——%@", [NSThread currentThread]);
});
NSLog(@"第一次结束——%@", [NSThread currentThread]);
// dispatch_barrier_async(queue, ^{
// NSLog(@"----------栅栏任务----------%@", [NSThread currentThread]);
// });
// NSLog(@"栅栏结束——%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"延迟1s的任务2——%@", [NSThread currentThread]);
});
NSLog(@"第二次结束——%@", [NSThread currentThread]);
}
复制代码
不使用栅栏函数
开始——<NSThread: 0x600002384f00>{number = 1, name = main}
第一次结束——<NSThread: 0x600002384f00>{number = 1, name = main}
第二次结束——<NSThread: 0x600002384f00>{number = 1, name = main}
延迟1s的任务2——<NSThread: 0x6000023ec300>{number = 5, name = (null)}
延迟2s的任务1——<NSThread: 0x60000238c180>{number = 7, name = (null)}
复制代码
使用栅栏函数
开始——<NSThread: 0x600000820bc0>{number = 1, name = main}
第一次结束——<NSThread: 0x600000820bc0>{number = 1, name = main}
栅栏结束——<NSThread: 0x600000820bc0>{number = 1, name = main}
第二次结束——<NSThread: 0x600000820bc0>{number = 1, name = main}
延迟2s的任务1——<NSThread: 0x600000863c80>{number = 4, name = (null)}
----------栅栏任务----------<NSThread: 0x600000863c80>{number = 4, name = (null)}
延迟1s的任务2——<NSThread: 0x600000863c80>{number = 4, name = (null)}
复制代码
并发队列异步执行
任务是乱序执行完毕的,因此使用栅栏函数能够很好的控制队列内任务执行的顺序
dispatch_barrier_async
:前面的任务执行完毕才会来到这里dispatch_barrier_sync
:做用相同,可是这个会堵塞线程,影响后面的任务执行将案例二中的dispatch_barrier_async
改为dispatch_barrier_sync
开始——<NSThread: 0x600001040d40>{number = 1, name = main}
第一次结束——<NSThread: 0x600001040d40>{number = 1, name = main}
延迟2s的任务1——<NSThread: 0x60000100ce40>{number = 6, name = (null)}
----------栅栏任务----------<NSThread: 0x600001040d40>{number = 1, name = main}
栅栏结束——<NSThread: 0x600001040d40>{number = 1, name = main}
第二次结束——<NSThread: 0x600001040d40>{number = 1, name = main}
延迟1s的任务2——<NSThread: 0x60000100ce40>{number = 6, name = (null)}
复制代码
结论:dispatch_barrier_async能够控制队列中任务的执行顺序,而dispatch_barrier_sync不只阻塞了队列的执行,也阻塞了线程的执行(尽可能少用)
尽可能使用自定义的并发队列
:
全局队列
起不到栅栏函数
的做用全局队列
时因为对全局队列形成堵塞,可能导致系统其余调用全局队列的地方也堵塞从而致使崩溃(并非只有你在使用这个队列)栅栏函数只能控制同一并发队列
:打个比方,平时在使用AFNetworking作网络请求时为何不能用栅栏函数起到同步锁堵塞的效果,由于AFNetworking内部有本身的队列应用场景:同步当锁, 控制GCD最大并发数
dispatch_semaphore_create()
:建立信号量dispatch_semaphore_wait()
:等待信号量,信号量减1。当信号量< 0
时会阻塞当前线程,根据传入的等待时间决定接下来的操做——若是永久等待将等到信号(signal)
才执行下去dispatch_semaphore_signal()
:释放信号量,信号量加1。当信号量>= 0
会执行wait以后的代码下面这段代码要求使用信号量来按序输出(固然栅栏函数能够知足要求)
- (void)test {
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"当前%d----线程%@", i, [NSThread currentThread]);
});
// 使用栅栏函数
// dispatch_barrier_async(queue, ^{});
}
}
复制代码
利用信号量的API来进行代码改写
- (void)test {
// 建立信号量
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("Felix", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"当前%d----线程%@", i, [NSThread currentThread]);
// 打印任务结束后信号量解锁
dispatch_semaphore_signal(sem);
});
// 因为异步执行,打印任务会较慢,因此这里信号量加锁
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
}
复制代码
输出结果
当前0----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前1----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前2----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前3----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前4----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前5----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前6----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前7----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前8----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
当前9----线程<NSThread: 0x600000c2c000>{number = 5, name = (null)}
复制代码
若是当建立信号量时传入值为1又会怎么样呢?
i=0
时有可能先打印
,也可能会先发出wait
信号量-1,可是wait
以后信号量为0不会阻塞线程,因此进入i=1
i=1
时有可能先打印
,也可能会先发出wait
信号量-1,可是wait
以后信号量为-1阻塞线程,等待signal
再执行下去当前1----线程<NSThread: 0x600001448d40>{number = 3, name = (null)}
当前0----线程<NSThread: 0x60000140c240>{number = 6, name = (null)}
当前2----线程<NSThread: 0x600001448d40>{number = 3, name = (null)}
当前3----线程<NSThread: 0x60000140c240>{number = 6, name = (null)}
当前4----线程<NSThread: 0x60000140c240>{number = 6, name = (null)}
当前5----线程<NSThread: 0x600001448d40>{number = 3, name = (null)}
当前6----线程<NSThread: 0x600001448d40>{number = 3, name = (null)}
当前7----线程<NSThread: 0x60000140c240>{number = 6, name = (null)}
当前8----线程<NSThread: 0x600001448d40>{number = 3, name = (null)}
当前9----线程<NSThread: 0x60000140c240>{number = 6, name = (null)}
复制代码
结论:
应用场景:GCDTimer
dispatch_source
是一种基本的数据类型,能够用来监听一些底层的系统事件
Timer Dispatch Source
:定时器事件源,用来生成周期性的通知或回调Signal Dispatch Source
:监听信号事件源,当有UNIX信号发生时会通知Descriptor Dispatch Source
:监听文件或socket事件源,当文件或socket数据发生变化时会通知Process Dispatch Source
:监听进程事件源,与进程相关的事件通知Mach port Dispatch Source
:监听Mach端口事件源Custom Dispatch Source
:监听自定义事件源主要使用的API:
dispatch_source_create
: 建立事件源dispatch_source_set_event_handler
: 设置数据源回调dispatch_source_merge_data
: 设置事件源数据dispatch_source_get_data
: 获取事件源数据dispatch_resume
: 继续dispatch_suspend
: 挂起dispatch_cancle
: 取消在iOS开发中通常使用NSTimer
来处理定时逻辑,但NSTimer
是依赖Runloop
的,而Runloop
能够运行在不一样的模式下。若是NSTimer
添加在一种模式下,当Runloop运行在其余模式下的时候,定时器就挂机了;又若是Runloop
在阻塞状态,NSTimer
触发时间就会推迟到下一个Runloop
周期。所以NSTimer
在计时上会有偏差,并非特别精确,而GCD定时器不依赖Runloop
,计时精度要高不少
@property (nonatomic, strong) dispatch_source_t timer;
//1.建立队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.建立timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//3.设置timer首次执行时间,间隔,精确度
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
//4.设置timer事件回调
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"GCDTimer");
});
//5.默认是挂起状态,须要手动激活
dispatch_resume(_timer);
复制代码
使用dispatch_source
自定义定时器注意点:
GCDTimer
须要强持有
,不然出了做用域当即释放,也就没有了事件回调GCDTimer
默认是挂起状态,须要手动激活GCDTimer
没有repeat
,须要封装来增长标志位控制GCDTimer
若是存在循环引用,使用weak+strong
或者提早调用dispatch_source_cancel
取消timerdispatch_resume
和dispatch_suspend
调用次数须要平衡source
在挂起状态
下,若是直接设置source = nil
或者从新建立source
都会形成crash。正确的方式是在激活状态
下调用dispatch_source_cancel(source)
释放当前的source
API | 说明 |
---|---|
dispatch_sync() | 同步执行 |
dispatch_async() | 异步执行 |
dispatch_queue_create() | 建立队列 |
dispatch_get_main_queue() | 获取主队列 |
dispatch_get_global_queue() | 获取全局队列 |
dispatch_after() | 延时执行 |
dispatch_once() | 一次性执行 |
dispatch_apply() | 提交队列 |
dispatch_group_create() | 建立调度组 |
dispatch_group_async() | 执行进组任务 |
dispatch_group_enter()/ dispatch_group_leave() |
将调度组中的任务未执行完毕的任务数目加减1 (两个函数要配合使用) |
dispatch_group_wait() | 设置等待时间(成功为0) |
dispatch_barrier_sync() | 同步栅栏函数 |
dispatch_barrier_async() | 异步栅栏函数 |
dispatch_group_notify() | 监听队列组执行完毕 |
dispatch_semaphore_creat() | 建立信号量 |
dispatch_semaphore_wait() | 等待信号量 |
dispatch_semaphore_signal() | 释放信号量 |
dispatch_source_create | 建立源 |
dispatch_source_set_event_handler | 设置源事件回调 |
dispatch_source_merge_data | 源事件设置数据 |
dispatch_source_get_data | 获取源事件数据 |
dispatch_resume | 继续 |
dispatch_suspend | 挂起 |
dispatch_cancle | 取消 |
下篇文章将探索GCD的底层原理,GCD应用中的部分坑点会在多线程面试题中进行补充