iOS当多个⽹络请求完成后执⾏下⼀步的⽅法详解
前⾔
在开发中,我们很容易遇到这样的需求,需要我们同时做多个⽹络请求,所有⽹络请求都完成后才能进⾏下⼀步的操作。
⽹络请求的任务是提交给⼦线程异步处理了,⽹络请求这样的任务也就快速执⾏完毕了,但是⽹络请求是⼀个任务,处理收到的⽹络响应⼜是⼀个任务,注意不要把这两个过程混为⼀谈。
如下载多个图⽚,下载完了才能展⽰,今天我们就来研究⼀下这个问题的解决⽅案。
解决⽅法
1.⾸先,我们创建⼀个项⽬,然后做⼀般性的做法,不做任何处理去连续请求⼀个接⼝10次:
先在viewDidLoad中创建第⼀种情况.
//1.⽆处理
UIButton *Btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
Btn1.frame = CGRectMake(100, 100, 100, 40);
Btn1.backgroundColor = [UIColor grayColor];
[Btn1 setTitle:@"noConduct" forState:UIControlStateNormal];
[Btn1 addTarget:self action:@selector(Btn1) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Btn1];
实现第⼀种情况的⽅法
//1.noConduct
-(void)Btn1{
NSString *str = @"www.jianshu/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {  NSLog(@"%d---%d",i,i);
}];
[task resume];
}
NSLog(@"end");
}
运⾏,看看我们的控制台输出:
2017-12-04 17:10:10.503 DownImage[3289:261033] end
2017-12-04 17:10:10.676 DownImage[3289:261080] 0---0
2017-12-04 17:10:10.704 DownImage[3289:261080] 1---1
2017-12-04 17:10:10.754 DownImage[3289:261096] 4---4
2017-12-04 17:10:10.760 DownImage[3289:261080] 2---2
2017-12-04 17:10:10.800 DownImage[3289:261096] 5---5
2017-12-04 17:10:10.840 DownImage[3289:261080] 7---7
2017-12-04 17:10:10.844 DownImage[3289:261082] 6---6
2017-12-04 17:10:10.846 DownImage[3289:261096] 3---3
2017-12-04 17:10:10.888 DownImage[3289:261096] 8---8
2017-12-04 17:10:10.945 DownImage[3289:261080] 9---9
很明显,⽆任何处理情况下,end最先被打印出来,由于⽹络请求的异步回调,然后各个⽹络请求的回调顺序是⽆序的。
2.使⽤GCD的dispatch_group_t
viewDidLoad⾥:
//2.group
UIButton *Btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
Btn2.frame = CGRectMake(100, 200, 100, 40);
Btn2.backgroundColor = [UIColor grayColor];
[Btn2 setTitle:@"group--" forState:UIControlStateNormal];
[Btn2 addTarget:self action:@selector(Btn2) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Btn2];
实现:
//2.group--
-
(void)Btn2{
NSString *str = @"www.jianshu/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%d---%d",i,i);
dispatch_group_leave(downloadGroup);
}];
[task resume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
运⾏看看控制台输出:
2017-12-04 17:14:46.984 DownImage[3289:265374] 2---2
2017-12-04 17:14:46.987 DownImage[3289:265370] 1---1
2017-12-04 17:14:47.052 DownImage[3289:265383] 5---5
2017-12-04 17:14:47.065 DownImage[3289:265370] 4---4
2017-12-04 17:14:47.111 DownImage[3289:265379] 3---3
2017-12-04 17:14:47.121 DownImage[3289:265383] 6---6
2017-12-04 17:14:47.169 DownImage[3289:265383] 7---7
2017-12-04 17:14:47.192 DownImage[3289:265370] 9---9
2017-12-04 17:14:47.321 DownImage[3289:265383] 8---8
2017-12-04 17:14:47.747 DownImage[3289:265374] 0---0
2017-12-04 17:14:47.747 DownImage[3289:261033] end
2017-12-04 17:15:14.576 DownImage[3289:265942] 3---3
2017-12-04 17:15:14.626 DownImage[3289:265936] 2---2
2017-12-04 17:15:14.647 DownImage[3289:265944] 4---4
2017-12-04 17:15:14.648 DownImage[3289:265936] 0---0
2017-12-04 17:15:14.657 DownImage[3289:265943] 1---1
2017-12-04 17:15:14.709 DownImage[3289:265944] 5---5
2017-12-04 17:15:14.728 DownImage[3289:265944] 6---6
2017-12-04 17:15:14.734 DownImage[3289:265944] 7---7
2017-12-04 17:15:14.738 DownImage[3289:265943] 8---8
2017-12-04 17:15:14.816 DownImage[3289:265944] 9---9
2017-12-04 17:15:14.816 DownImage[3289:261033] end
从上两次输出可以看出,end确实是在所有⽹络请求之后才输出出来,这也是符合了我们的需求。
代码中我们只添加了4⾏代码
dispatch_group_t downloadGroup = dispatch_group_create();
dispatch_group_enter(downloadGroup);
dispatch_group_leave(downloadGroup);
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
});
对以上4⾏代码可理解为:创建⼀个dispatch_group_t,每次⽹络请求前先dispatch_group_enter,请求回调后再dispatch_group_leave,对于enter和leave必须配合使⽤,有⼏次enter就要有⼏次leave,否则group会⼀直存在。当所有enter的block都leave后,会执⾏dispatch_group_notify的block。
3.使⽤GCD的信号量dispatch_semaphore_t
//3.semaphore
UIButton *Btn3 = [UIButton buttonWithType:UIButtonTypeCustom];
Btn3.frame = CGRectMake(100, 300, 100, 40);
Btn3.backgroundColor = [UIColor grayColor];
[Btn3 setTitle:@"semaphore" forState:UIControlStateNormal];
[Btn3 addTarget:self action:@selector(Btn3) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Btn3];
//3.semaphore--
-(void)Btn3{
NSString *str = @"www.jianshu/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {  NSLog(@"%d---%d",i,i);
count++;
if (count==10) {
dispatch_semaphore_signal(sem);
count = 0;
}
}];
[task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
运⾏看看控制台输出:
2017-12-04 17:36:49.098 DownImage[3428:283651] 2---2
2017-12-04 17:36:49.144 DownImage[3428:284210] 0---0
2017-12-04 17:36:49.152 DownImage[3428:284213] 3---3
2017-12-04 17:36:49.158 DownImage[3428:283651] 1---1
2017-12-04 17:36:49.167 DownImage[3428:284210] 4---4
2017-12-04 17:36:49.235 DownImage[3428:284213] 8---8
2017-12-04 17:36:49.249 DownImage[3428:283651] 5---5
2017-12-04 17:36:49.252 DownImage[3428:283651] 7---7
2017-12-04 17:36:49.324 DownImage[3428:283651] 9---9
2017-12-04 17:36:49.468 DownImage[3428:284214] 6---6
2017-12-04 17:36:49.469 DownImage[3428:283554] end
2017-12-04 17:37:11.554 DownImage[3428:284747] 0---0
2017-12-04 17:37:11.555 DownImage[3428:284733] 1---1
2017-12-04 17:37:11.627 DownImage[3428:284748] 5---5
2017-12-04 17:37:11.661 DownImage[3428:284748] 2---2
2017-12-04 17:37:11.688 DownImage[3428:284747] 4---4
2017-12-04 17:37:11.709 DownImage[3428:284747] 6---6
2017-12-04 17:37:11.770 DownImage[3428:284733] 7---7
2017-12-04 17:37:11.774 DownImage[3428:284733] 8---8
2017-12-04 17:37:11.824 DownImage[3428:284747] 9---9
2017-12-04 17:37:11.899 DownImage[3428:284733] 3---3
2017-12-04 17:37:11.900 DownImage[3428:283554] end
从输出可以看出,这样的⽅法也是满⾜我们的需求的,在这个⽅法中,我们使⽤了
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_semaphore_signal(sem);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
对这三句代码可以这样理解:dispatch_semaphore信号量为基于计数器的⼀种多线程同步机制。如果semaphore计数⼤于等于1,计数-1,返回,程序继续运⾏。如果计数为0,则等待。dispatch_semaphore_signal(semaphore)为计数+1操作,dispatch_semaphore_wait(sema, DISPATCH_
TIME_FOREVER)为设置等待时间,这⾥设置的等待时间是⼀直等待。
对于以上代码通俗⼀点就是,开始为0,等待,等10个⽹络请求都完成了,dispatch_semaphore_signal(semaphore)为计数+1,然后计数-1返回,程序继续执⾏。 (这⾥也就是为什么有个count变量的原因,记录⽹络回调的次数,回调10次之后再发信号量,使后⾯程序继续运⾏)。
4.考虑新需求,10个⽹络请求顺序回调。
需求需要顺序回调,即执⾏完第⼀个⽹络请求后,第⼆个⽹络请求回调才可被执⾏,简单来讲就是输出得是0,1,9这种⽅式的。
对于这个需求我也是根据⾃⼰最近做的项⽬来提的,因为⽹络请求回调的异步性,我们虽可以控制⽹络请求的顺序执⾏,却不能控制它的完成回调顺序。
这就有点伤了,⽬前我项⽬是到了解决⽅案,但这个问题还没有到解决办法,提出来跟⼤家讨论⼀下。(请忽略⽹络请求执⾏,回调,在回调⾥请求下
⼀个接⼝的办法,讨论还有没有别的⽅法,最好show the code).
最后,贴点NSOperation的代码,为了解决新需求所写,由于⽹络请求回调异步性不能满⾜需求,但若不是⽹络请求等异步回调的⽅式,这样的做法是可以的,⼤家可以试试.
//4.NSOperation
UIButton *Btn4 = [UIButton buttonWithType:UIButtonTypeCustom];
Btn4.frame = CGRectMake(100, 400, 100, 40);
Btn4.backgroundColor = [UIColor grayColor];
[Btn4 setTitle:@"NSOperation" forState:UIControlStateNormal];
[Btn4 addTarget:self action:@selector(Btn4) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Btn4];
//4.NSOperation
-(void)Btn4{
NSString *str = @"www.jianshu/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableArray *operationArr = [[NSMutableArray alloc]init];
for (int i=0; i<10; i++) {
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {    NSLog(@"%d---%d",i,i);
}];
[task resume];
//⾮⽹络请求
NSLog(@"noRequest-%d",i);
}];
[operationArr addObject:operation];
if (i>0) {
NSBlockOperation *operation1 = operationArr[i-1];
NSBlockOperation *operation2 = operationArr[i];
[operation2 addDependency:operation1];
}
}
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperations:operationArr waitUntilFinished:NO]; //YES会阻塞当前线程
#warning - 绝对不要在应⽤主线程中等待⼀个Operation,只能在第⼆或次要线程中等待。阻塞主线程将导致应⽤⽆法响应⽤户事件,应⽤也将表现为⽆响应。
}
运⾏结果:
2017-12-04 18:03:10.224 DownImage[3584:304363] noRequest-0
2017-12-04 18:03:10.226 DownImage[3584:304362] noRequest-1
2017-12-04 18:03:10.226 DownImage[3584:304363] noRequest-2
2017-12-04 18:03:10.231 DownImage[3584:304363] noRequest-3
2017-12-04 18:03:10.232 DownImage[3584:304362] noRequest-4
2017-12-04 18:03:10.233 DownImage[3584:304362] noRequest-5
2017-12-04 18:03:10.233 DownImage[3584:304363] noRequest-6
2017-12-04 18:03:10.234 DownImage[3584:304363] noRequest-7
2017-12-04 18:03:10.235 DownImage[3584:304363] noRequest-8
2017-12-04 18:03:10.236 DownImage[3584:304363] noRequest-9
2017-12-04 18:03:10.408 DownImage[3584:304597] 2---2
2017-12-04 18:03:10.408 DownImage[3584:304597] 0---0
2017-12-04 18:03:10.409 DownImage[3584:304597] 1---1
2017-12-04 18:03:10.461 DownImage[3584:304597] 5---5
2017-12-04 18:03:10.476 DownImage[3584:304363] 4---4
2017-12-04 18:03:10.477 DownImage[3584:304365] 6---6
2017-12-04 18:03:10.518 DownImage[3584:304365] 7---7
2017-12-04 18:03:10.537 DownImage[3584:304596] 8---8
2017-12-04 18:03:10.547 DownImage[3584:304362] 9---9
2017-12-04 18:03:11.837 DownImage[3584:304362] 3---3
2017-12-04 18:04:27.699 DownImage[3584:306401] noRequest-0
2017-12-04 18:04:27.700 DownImage[3584:306405] noRequest-1
2017-12-04 18:04:27.701 DownImage[3584:306401] noRequest-2
2017-12-04 18:04:27.701 DownImage[3584:306405] noRequest-3
2017-12-04 18:04:27.702 DownImage[3584:306401] noRequest-4
2017-12-04 18:04:27.702 DownImage[3584:306405] noRequest-5
2017-12-04 18:04:27.703 DownImage[3584:306401] noRequest-6
2017-12-04 18:04:27.703 DownImage[3584:306401] noRequest-7
2017-12-04 18:04:27.704 DownImage[3584:306401] noRequest-8
2017-12-04 18:04:27.704 DownImage[3584:306401] noRequest-9
2017-12-04 18:04:27.772 DownImage[3584:306397] 2---2
2017-12-04 18:04:27.779 DownImage[3584:306401] 0---0
2017-12-04 18:04:27.782 DownImage[3584:306409] 1---1
2017-12-04 18:04:27.800 DownImage[3584:306405] 3---3
2017-12-04 18:04:27.851 DownImage[3584:306401] 6---6
2017-12-04 18:04:27.855 DownImage[3584:306397] 5---5
2017-12-04 18:04:27.915 DownImage[3584:306397] 7---7
2017-12-04 18:04:27.951 DownImage[3584:306397] 9---9
2017-12-04 18:04:27.953 DownImage[3584:306405] 8---8
2017-12-04 18:04:28.476 DownImage[3584:306409] 4---4
5.还是使⽤信号量semaphore完成4的需求
//5.semaphore---order
UIButton *Btn5 = [UIButton buttonWithType:UIButtonTypeCustom];
Btn5.frame = CGRectMake(100, 500, 100, 40);
Btn5.backgroundColor = [UIColor grayColor];
[Btn5 setTitle:@"order" forState:UIControlStateNormal];
[Btn5 addTarget:self action:@selector(Btn5) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Btn5];
//5.semaphore--order
-(void)Btn5{
NSString *str = @"www.jianshu/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {  NSLog(@"%d---%d",i,i);
dispatch_semaphore_signal(sem);
}];
[task resume];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
我们看看运⾏结果:
2017-12-05 10:17:28.175 DownImage[938:51296] 0---0
2017-12-05 10:17:28.331 DownImage[938:51289] 1---1
2017-12-05 10:17:28.506 DownImage[938:51289] 2---2
2017-12-05 10:17:28.563 DownImage[938:51289] 3---3
2017-12-05 10:17:28.662 DownImage[938:51289] 4---4
2017-12-05 10:17:28.733 DownImage[938:51296] 5---5
2017-12-05 10:17:28.792 DownImage[938:51296] 6---6
2017-12-05 10:17:28.856 DownImage[938:51286] 7---7
2017-12-05 10:17:29.574 DownImage[938:51289] 8---8
session下载2017-12-05 10:17:29.652 DownImage[938:51286] 9---9
2017-12-05 10:17:29.653 DownImage[938:45252] end
2017-12-05 10:17:46.341 DownImage[938:51608] 0---0
2017-12-05 10:17:47.967 DownImage[938:51607] 1---1
2017-12-05 10:17:48.038 DownImage[938:51603] 2---2
2017-12-05 10:17:48.132 DownImage[938:51603] 3---3
2017-12-05 10:17:48.421 DownImage[938:51608] 4---4
2017-12-05 10:17:48.537 DownImage[938:51289] 5---5
2017-12-05 10:17:48.646 DownImage[938:51289] 6---6
2017-12-05 10:17:48.939 DownImage[938:51289] 7---7
2017-12-05 10:17:50.537 DownImage[938:51607] 8---8
2017-12-05 10:17:50.615 DownImage[938:51289] 9---9
2017-12-05 10:17:50.616 DownImage[938:45252] end
我们对⽐ 3 的代码,3 中我们只在最后也就是循环结束后调⽤dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) ,循环中当⽹络请求回调10次(也就是都回调完)后,使传⼊的信号量加1:( dispatch_semaphore_signal(sem) ) ,这时等待结束,然后进⾏后续的操作。
在这个⽅法⾥,我们每⼀次遍历,都让其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),这个时候线程会等待,阻塞当前线程,直
到dispatch_semaphore_signal(sem)调⽤之后,⽽我们dispatch_semaphore_signal(sem)是在⽹络请求的回调⾥调⽤的,所以这个⽅法的逻辑是:
遍历—>发起任务—>等待—>任务完成信号量加1—>等待结束,开始下⼀个任务
发起任务—>等待—>任务完成信号量加1—>等待结束,开始下⼀个任务
发起任务—>等待—>任务完成信号量加1—>等待结束,开始下⼀个任务
这样循环的模式,⼀个任务完成才能接着去做下⾯的任务,满⾜我们的需求。
但我们也要发现这样⼀个问题,我们使⽤这种⽅式,可以明显感觉出整个过程需要花费的时间⼤⼤增加了,不像我们 3 中同时(⼏乎)开启任务等待完成回调,这⾥是⼀个⽹络请求发出,等待,完成后发出第⼆个⽹络请求,等待,完成后再发出第三个,这样我们等待的时间是10个⽹络请求每⼀个回调时间的和,在时间上⼤⼤增加了消耗,⽽且对于dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),它是会阻塞线程的,我们如果需要在⽹络请求完成后修改UI,那这种⽅式会影响我们的界⾯交互,接下来我们对⽐⼀下两者时间消耗:
3-------------3----------3-------
2017-12-05 10:29:51.178 DownImage[938:56971] 2---2
2017-12-05 10:29:51.193 DownImage[938:57200] 0---0
2017-12-05 10:29:51.202 DownImage[938:56631] 3---3
2017-12-05 10:29:51.248 DownImage[938:56971] 1---1
2017-12-05 10:29:51.262 DownImage[938:56971] 5---5
2017-12-05 10:29:51.291 DownImage[938:56631] 6---6
2017-12-05 10:29:51.375 DownImage[938:56631] 7---7
2017-12-05 10:29:51.384 DownImage[938:56631] 4---4
2017-12-05 10:29:51.434 DownImage[938:56971] 8---8
2017-12-05 10:29:51.487 DownImage[938:57199] 9---9
2017-12-05 10:29:51.488 DownImage[938:45252] end
5-------------5----------5-------
2017-12-05 10:29:52.190 DownImage[938:56631] 0---0
2017-12-05 10:29:52.304 DownImage[938:57199] 1---1
2017-12-05 10:29:52.432 DownImage[938:56971] 2---2
2017-12-05 10:29:52.520 DownImage[938:56971] 3---3
2017-12-05 10:29:52.576 DownImage[938:56631] 4---4
2017-12-05 10:29:52.628 DownImage[938:56971] 5---5
2017-12-05 10:29:52.706 DownImage[938:56631] 6---6
2017-12-05 10:29:52.764 DownImage[938:56971] 7---7
2017-12-05 10:29:52.853 DownImage[938:56631] 8---8
2017-12-05 10:29:52.925 DownImage[938:56971] 9---9
2017-12-05 10:29:52.926 DownImage[938:45252] end
看得出3花费时间为51.488 - 51.178约300多ms
--- ---5花费时间为52.926 - 52.190约700多ms
所以⼤家还请谨慎使⽤。
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。