实际开发中,网络数据的并发请求是一个非常常见的需求,在一次需要使用多个网络接口时,并发请求相对于串行请求有节省时间,速度快的优势。但有时我们也会有需要所有接口的数据全部获取之后再进行下一步操作的需求,比如登录流程中,多接口数据全部获取完毕才显示登录成功,某一个复杂页面,需要所有数据获取完毕才进行视图的绘制或刷新。今天本文就想用
dispatch_group
来实现这个需求。
曾经的落后方案
落后方案1 串行嵌套方案
串行嵌套方案应该属于最常规的方案,将多个网络接口嵌套请求,一个网络请求数据获取成功之后,在进行下一个网络请求。
[网络请求1:{
成功或失败:网络请求2];
[网络请求2:{
成功或失败:网络请求3];
[网络请求3:{
成功或失败:网络请求4];
[网络请求4:{
成功或失败:next step];
该方案的缺陷还比较明显,请求数据的积累耗时非常严重,两三个接口也许还好,如果一次需要获取八九个接口数据之后,才进行下一步操作,那么也许需要等待数秒的时间,这样的产品体验是非常糟糕的。
从代码层面说,嵌套的方式可读性并不好,复杂不容易读懂。
所以该方案确实很落后。。
落后方案2 BOOL值监控法
该方案是本人接触dispatch_group
前,自创的并发请求数据获取监控方式,如有雷同,全属巧合。
针对每一个请求设置一个对应的BOOL
值,待每一个BOOL
值都为YES
时,执行下一步操作。
BOOL reqOneCompletion;
BOOL reqTwoCompletion;
BOOL reqThreeCompletion;
- (void)network {
[网络请求1:{
成功或失败:reqOneCompletion = YES
[self nextStep]];
[网络请求2:{
成功或失败:reqTwoCompletion = YES
[self nextStep]];
[网络请求3:{
成功或失败:reqThreeCompletion = YES
[self nextStep]];
}
- (void)nextStep {
if (reqOneCompletion && reqTwoCompletion && reqThreeCompletion) {
// 执行一下步操作
}
}
在该方案中,本人通过设置BOOL值和网络请求任务之间的映射关系,监控网络请求的数据是否返回完毕,最后再判断是否可以执行下一步操作。该方案虽然具有可行性,但代码的可读性上依旧比较糟糕,网络请求任务和BOOL之间的映射关系存在出错的可能性,虽然逻辑上和后文讲解dispatch_group
一致,但比较土味,我们还是需要更潮一些的方法。
使用dispatch_group方案
dispatch_group是GCD(Grand Central Dispatch)中的一组方法,他有一个组的概念,可以把相关的任务归并到一个组内来执行,通过监听组内所有任务的执行情况来做相应处理。
dispatch_group的使用主要是4个方法
- dispatch_group_create
- dispatch_group_async
- dispatch_group_enter
- dispatch_group_leave
- dispatch_group_notify
dispatch_group_create
创建任务组,创建任务组后才能把任务加入进组内。
dispatch_group_t dispatch_group_create(void);
dispatch_group_async
通过异步执行任务。
void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
- group :对应的任务组,之后可以通过
dispatch_group_wait
或者dispatch_group_notify
监听任务组内任务的执行情况 - queue :block任务执行的线程队列,任务组内不同任务的队列可以不同
- block : 执行任务的block
dispatch_group_enter
用于添加对应任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数加1,当未执行完毕任务数为0的时候,才会使dispatch_group_wait
解除阻塞和dispatch_group_notify
的block执行。
void dispatch_group_enter(dispatch_group_t group);
dispatch_group_leave
用于减少任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数减1,dispatch_group_ente
r和dispatch_group_leave
要匹配,不然系统会认为group任务没有执行完毕。
void dispatch_group_leave(dispatch_group_t group);
dispatch_group_notify
用于监控group中的任务数,任务数归0时,方法执行。这个也是并发请求关键一步,当他执行时代表并发请求数据获取完毕。
void dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
- group :需要监听的任务组
- queue :block任务执行的线程队列,和之前group执行的线程队列无关
- block :任务组执行完毕时需要执行的任务block
dispatch_group_wait
等待组任务完成,会阻塞当前线程,当任务组执行完毕时,才会解除阻塞当前线程。
long dispatch_group_wait(dispatch_group_t group,
dispatch_time_t timeout);
- group :需要等待的任务组
- timeout :等待的超时时间(即等多久),单位为dispatch_time_t。如果设置为DISPATCH_TIME_FOREVER,则会一直等待(阻塞当前线程),直到任务组执行完毕
以上dispatch_group的方法介绍完毕,我们来看看具体使用的方式。
实际使用:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//请求1
[网络请求1:{
成功或失败:dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//请求2
[网络请求2:{
成功或失败:dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//请求3
[网络请求3:{
成功或失败:dispatch_group_leave(group);
}];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//界面刷新
next step
});
该并发网络请求方案的优势相对于上文介绍的两种落后的方式来说就很明显了。异步请求,更加的节省网络请求等待的时间,流畅度和体验上更加好。可读性方面,不用开发者自己去写一些映射关系作为监控,使用GCD自带的组内任务计数,更加的准确,方便,代码读起来也更加的直观。👍👍👍