在前一篇文章,我们介绍了GCD的基本使用,其中就包含分发函数这一节,详细介绍了dispatch_async和dispatch_sync两个分发函数。今天我们就来探讨一下更多的分发函数,从而体验GCD中更高级的功能。对于分发函数中_f结尾的函数,不再进行详细介绍,有_f和没有_f的分发函数,不同点就是_f是把任务封装成C函数,而不带_f是把任务封装成block。
GCD的任务分发函数除了之前介绍的两个之外,还有以下几个:dispatch_after、dispatch_apply和dispatch_once,每个函数的名称都突出了函数的功能,学习这些函数是,可以注意函数名及其功能之间的关系,以增强理解。下面我们一一的学习这几个分发函数。
1. dispatch_after,这个函数的声明是:
void dispatch_after ( dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block );
可以看出,这个函数只是比dispatch_async多了一个参数(when)。其实,这个函数的功能跟dispatch_async也很类似,此函数也是异步分发函数,调用之后会立即返回,无需等待分发的任务执行完成。但是,此函数相对于dispatch_async增加了一项功能,就是延时分发,即等待when参数给定的时间之后,在把任务分发到对应的queue中。等待时间参数(when)可以通过dispatch_time函数获取。具体使用如下:
// 获取等待时间参数值,其中DISPATCH_TIME_NOW表示延时的开始计算时间(此处为当前时间),NSEC_PER_SEC代表延时的时间单位(此处为秒),10代表时长
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
// 把任务封装成block,延时10秒分发到mainQueue中
dispatch_after(when, dispatch_get_main_queue(), ^{
// 待执行的任务代码
});
注意:如果延时时间为0,直接使用dispatch_async会有更高的效率。
2.dispatch_apply,函数的声明如下:
void dispatch_apply ( size_t iterations, dispatch_queue_t queue, void (^block)(size_t) );
dispatch_apply就是进行循环分发,把任务循环分发指定次数到相应的队列中。此函数也是异步分发,函数会立即返回,无需等待任务执行完成。下面是dispatch_apply的应用,及与其等效的for循环实现:
// 循环10次分发block,block中有一个参数,是当前循环的index。相当于for循环中的i
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t index) {
// 待执行的任务代码
});
// 循环10次,与上面dispatch_apply的调用等价。但是建议使用dispatch_apply模式
for (int i = 0; i < 10; i++) {
// 异步分发任务
dispatch_async(dispatch_get_main_queue(), ^{
// 待执行的任务代码
});
}
2.dispatch_once,函数的声明如下:
void dispatch_once ( dispatch_once_t *predicate, dispatch_block_t block );
这个函数的作用是保证在程序运行生命周期内,分发任务只执行一次。当多线程同时调用此函数时,所有都会被阻塞住,直到分发任务执行完成,然后被阻塞的线程会继续执行。dispatch_once函数的第一个参数用于标识分发任务的执行过程,分发任务是否执行完成都是通过此标识来判断,故此标识必须是一个全局变量或static变量。否则每次都是一个新的标识变量,dispatch_once无法判断分发任务已执行,从而每次调用都会执行分发任务,失去了只需执行一次的意义。
// 注意,dispatch_once_t是一个static变量
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只需执行一次的代码
});
dispatch_once一个很大的用处,就是实现单例模式,其能保证代码在程序运行生命周期只执行一次,如果把创建单例的代码放在dispatch_once中执行,则能保证单例创建代码只执行一次,只会创建一个实例。