现在面对一个需求,需要在当前控制器中请求多个网络接口,等所有请求回调成功后在用数据刷新界面UI。
1.普通解决方案
可能大家会想用一个临时的布尔值去记录每个请求的回调成功与否的状态,最后再通过判断每个请求对应的状态值来决定刷新UI的时机。请求少(一个两个)的情况还可行,但是当请求过多就有问题了:每个接口的回调部分都需要对其请求的状态布尔值赋值,然后判断是否所有请求的状态值都为完成状态,代码缀余情况严重,切可读性极差。最重要的是low!!
2.GCD方案
a.信号量
基本函数:
dispatch_semaphore_create(long value)
该函数使用一个初始值创建一个dispatch_semaphore_t类型的信号量,注意:这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL
dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull dsema)
发送一个信号,这个函数会使传入的信号量dsema的值加1
dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull dsema, dispatch_time_t timeout)
等待信号量,该函数会使传入的信号量dsema的值减1,当第一个函数创建出来的信号量值大于0时,本函数后面的代码就会执行,为0时则等待,timeout则是等待超时时间
上代码:
_sem1 = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"请求 1完成");
dispatch_semaphore_signal(_sem1);
});
dispatch_async(queue, ^{
sleep(3);
NSLog(@"请求 2完成");
dispatch_semaphore_signal(_sem1);
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"请求 3完成");
dispatch_semaphore_signal(_sem1);
});
NSLog(@"第一次等待信号");
dispatch_semaphore_wait(_sem1, DISPATCH_TIME_FOREVER);
NSLog(@"第二次等待信号");
dispatch_semaphore_wait(_sem1, DISPATCH_TIME_FOREVER);
NSLog(@"第三次等待信号");
dispatch_semaphore_wait(_sem1, DISPATCH_TIME_FOREVER);
NSLog(@"所有请求完成");
打印结果:
2017-08-18 16:44:48.505439+0800 QUEUEtest[1427:258357] 第一次等待信号
2017-08-18 16:44:49.507970+0800 QUEUEtest[1427:258398] 请求 1完成
2017-08-18 16:44:49.508283+0800 QUEUEtest[1427:258357] 第二次等待信号
2017-08-18 16:44:50.511092+0800 QUEUEtest[1427:258402] 请求 3完成
2017-08-18 16:44:50.511338+0800 QUEUEtest[1427:258357] 第三次等待信号
2017-08-18 16:44:51.509610+0800 QUEUEtest[1427:258401] 请求 2完成
2017-08-18 16:44:51.509831+0800 QUEUEtest[1427:258357] 所有请求完成
代码中使用线程睡眠来模拟网络耗时请求,根据设定时间,完成的请求顺序为1>3>2,打印结果也是如此。
b.任务组
基本函数:
dispatch_group_create()
新建一个任务组
dispatch_group_enter(dispatch_group_t _Nonnull group)
通知group,下面的任务马上要放到group中执行了
dispatch_group_leave(dispatch_group_t _Nonnull group)
通知group,任务完成了,该任务要从group中移除了
dispatch_group_notify(dispatch_group_t _Nonnull group, dispatch_queue_t _Nonnull queue, ^{ //所有请求完成后的操作 })
当group中所有任务都结束后自动调用此方法
上代码:
dispatch_group_t semaphore = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("Queue", DISPATCH_QUEUE_SERIAL);
dispatch_group_enter(semaphore);
dispatch_group_async(semaphore, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求1完成");
dispatch_group_leave(semaphore);
});
});
dispatch_group_enter(semaphore);
dispatch_group_async(semaphore, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求2完成");
dispatch_group_leave(semaphore);
});
});
dispatch_group_enter(semaphore);
dispatch_group_async(semaphore, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求3完成");
dispatch_group_leave(semaphore);
});
});
dispatch_group_notify(semaphore, queue, ^{
//这里就是所有异步任务请求结束后执行的代码
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"所有请求完成(刷新UI进主线程)");
});
});
打印结果:
2017-08-18 17:04:04.639729+0800 QUEUEtest[1434:261035] 请求3完成
2017-08-18 17:04:04.857239+0800 QUEUEtest[1434:261035] 请求1完成
2017-08-18 17:04:04.857448+0800 QUEUEtest[1434:261035] 请求2完成
2017-08-18 17:04:04.857822+0800 QUEUEtest[1434:261035] 所有请求完成(刷新UI进主线程)
这里也是加入了线程延时来模拟网络耗时请求,任务组方法其本质上也是信号量的运用,只是苹果官方在信号量的基础上封装了一层,越上层的方法越容易上手,越底层的方法越可以根据需求自由变换应用,各有千秋。
本文章就不上demo了,上面代码直接可以粘贴运行,一起学习,共同进步!