1、问题
在开发中,我们会经常遇到这样的需求,需要我们同时做多个网络请求,然后架构所需数据,再统一渲染页面。
2、解决方案
2.1、一般方式:不做任何处理连续请求多个协议
viewDidLoad添加按钮:
//1.无处理 UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom]; btn1.frame = CGRectMake(100, 100, 100, 40); btn1.backgroundColor = [UIColor grayColor]; [btn1 setTitle:@"Respective" forState:UIControlStateNormal]; [btn1 addTarget:self action:@selector(btn1Action) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn1];
实现:
//1.Respective -(void)btn1Action{ NSString *str = @"https://blog.csdn.net/weixin_38633659"; 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"); }
控制台输出:
2021-02-24 10:20:10.503643+0800 TestDemo[18587:9715251] end 2021-02-24 10:20:10.676472+0800 TestDemo[18587:9715251] 0---0 2021-02-24 10:20:10.704834+0800 TestDemo[18587:9715251] 1---1 2021-02-24 10:20:10.754361+0800 TestDemo[18587:9715251] 4---4 2021-02-24 10:20:10.760842+0800 TestDemo[18587:9715251] 2---2 2021-02-24 10:20:10.800468+0800 TestDemo[18587:9715251] 5---5 2021-02-24 10:20:10.840135+0800 TestDemo[18587:9715251] 7---7 2021-02-24 10:20:10.844221+0800 TestDemo[18587:9715251] 6---6 2021-02-24 10:20:10.846853+0800 TestDemo[18587:9715251] 3---3 2021-02-24 10:20:10.888963+0800 TestDemo[18587:9715251] 8---8 2021-02-24 10:20:10.945394+0800 TestDemo[18587:9715251] 9---9
分析:
1、无任何处理情况下,end最先被打印出来。
2、由于请求是异步的,然后各个网络请求的回调顺序是无序的,无法监听最后一次结束,进行回调后的统一处理。
3、该场景适合协议和后续处理互相独立的,没有影响的。
应用场景:
UI格式固定,只需要更新服务器下发数据,这时候就可以用各自渲染实现,互不影响。
2.2、GCD队列组:dispatch_group_t
viewDidLoad添加按钮:
//2.GCD队列组 UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom]; btn2.frame = CGRectMake(100, 100, 120, 40); btn2.backgroundColor = [UIColor grayColor]; [btn2 setTitle:@"group--" forState:UIControlStateNormal]; [btn2 addTarget:self action:@selector(btn2Action) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn2];
实现:
/// 2.GCD队列组 - (void)btn2Action { NSString *str = @"https://blog.csdn.net/weixin_38633659"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; __block NSMutableArray *responseDataMarr = [NSMutableArray arrayWithCapacity:0]; dispatch_group_t downloadGroup = dispatch_group_create(); for (NSInteger i = 0; i < 10; i ++) { dispatch_group_enter(downloadGroup); NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { [responseDataMarr addObject:[NSString stringWithFormat:@"%ld:%@",i,response.URL.absoluteString]]; NSLog(@"%ld------------group",i); dispatch_group_leave(downloadGroup); }]; [task resume]; } dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ NSLog(@"end----------%@",responseDataMarr); }); }
控制台输出1:
2021-02-24 10:24:36.104843+0800 TestDemo[18587:9712251] 4------------group 2021-02-24 10:24:36.109190+0800 TestDemo[18587:9712251] 8------------group 2021-02-24 10:24:36.160072+0800 TestDemo[18587:9712688] 2------------group 2021-02-24 10:24:36.217361+0800 TestDemo[18587:9712690] 0------------group 2021-02-24 10:24:36.263296+0800 TestDemo[18587:9712183] 6------------group 2021-02-24 10:24:36.275118+0800 TestDemo[18587:9712251] 1------------group 2021-02-24 10:24:36.320592+0800 TestDemo[18587:9712251] 9------------group 2021-02-24 10:24:36.377655+0800 TestDemo[18587:9712688] 7------------group 2021-02-24 10:24:36.385682+0800 TestDemo[18587:9712183] 3------------group 2021-02-24 10:24:36.422906+0800 TestDemo[18587:9712183] 5------------group 2021-02-24 10:24:36.425515+0800 TestDemo[18587:9711955] end----------( "4:https://blog.csdn.net/weixin_38633659", "8:https://blog.csdn.net/weixin_38633659", "2:https://blog.csdn.net/weixin_38633659", "0:https://blog.csdn.net/weixin_38633659", "6:https://blog.csdn.net/weixin_38633659", "1:https://blog.csdn.net/weixin_38633659", "9:https://blog.csdn.net/weixin_38633659", "7:https://blog.csdn.net/weixin_38633659", "3:https://blog.csdn.net/weixin_38633659", "5:https://blog.csdn.net/weixin_38633659" )
控制台输出2:
2021-02-24 10:27:51.144579+0800 TestDemo[18587:9713318] 4------------group 2021-02-24 10:27:51.201340+0800 TestDemo[18587:9713361] 3------------group 2021-02-24 10:27:51.241146+0800 TestDemo[18587:9713359] 6------------group 2021-02-24 10:27:51.249819+0800 TestDemo[18587:9713361] 9------------group 2021-02-24 10:27:51.254865+0800 TestDemo[18587:9713361] 1------------group 2021-02-24 10:27:51.259506+0800 TestDemo[18587:9713361] 0------------group 2021-02-24 10:27:51.269500+0800 TestDemo[18587:9713360] 7------------group 2021-02-24 10:27:51.289864+0800 TestDemo[18587:9713263] 2------------group 2021-02-24 10:27:51.303468+0800 TestDemo[18587:9713263] 8------------group 2021-02-24 10:27:51.307422+0800 TestDemo[18587:9713318] 5------------group 2021-02-24 10:27:51.308330+0800 TestDemo[18587:9711955] end----------( "4:https://blog.csdn.net/weixin_38633659", "3:https://blog.csdn.net/weixin_38633659", "6:https://blog.csdn.net/weixin_38633659", "9:https://blog.csdn.net/weixin_38633659", "1:https://blog.csdn.net/weixin_38633659", "0:https://blog.csdn.net/weixin_38633659", "7:https://blog.csdn.net/weixin_38633659", "2:https://blog.csdn.net/weixin_38633659", "8:https://blog.csdn.net/weixin_38633659", "5:https://blog.csdn.net/weixin_38633659" )
分析:
1、从上两次输出可以看出,end都是在所有网络请求之后才打印出来。
2、协议都是异步处理,互相独立,只有等全部协议请求完成之后,再统一处理
适用场景:
这种情况适合前后协议请求没有绝对的逻辑要求,而是对最后请求返回的数据需要统一整理,再渲染数据。
原理:
代码中我们只添加了4行代码
dispatch_group_t downloadGroup = dispatch_group_create(); dispatch_group_enter(downloadGroup); dispatch_group_leave(downloadGroup); dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 统一返回处理 });
1、dispatch_group_create:创建一个group,初始计数为0;
2、dispatch_group_enter:负责将group的计数+1;
3、dispatch_group_leave:负责将group的计数-1;
4、dispatch_group_notify:监听group计数,当group计数为0时,执行里面的操作。
2.3、GCD信号量:dispatch_semaphore_t
viewDidLoad添加按钮:
//3.semaphore UIButton *btn3 = [UIButton buttonWithType:UIButtonTypeCustom]; btn3.frame = CGRectMake(100, 200, 120, 40); btn3.backgroundColor = [UIColor grayColor]; [btn3 setTitle:@"semaphore" forState:UIControlStateNormal]; [btn3 addTarget:self action:@selector(btn3Action) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn3];
实现:
/// 3.semaphore-- - (void)btn3Action { NSString *str = @"https://blog.csdn.net/weixin_38633659"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; __block NSMutableArray *responseDataMarr = [NSMutableArray arrayWithCapacity:0]; dispatch_semaphore_t sem = dispatch_semaphore_create(0); for (NSInteger i = 0; i < 10; i ++) { NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { [responseDataMarr addObject:[NSString stringWithFormat:@"%ld:%@",i,response.URL.absoluteString]]; dispatch_semaphore_signal(sem); NSLog(@"%ld------------semaphore",i); }]; [task resume]; dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); } dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"end----------%@",responseDataMarr); }); }
控制台输出:
2021-02-24 10:24:09.656653+0800 TestDemo[18587:9712527] 0------------semaphore 2021-02-24 10:24:09.918128+0800 TestDemo[18587:9712251] 1------------semaphore 2021-02-24 10:24:10.129609+0800 TestDemo[18587:9712251] 2------------semaphore 2021-02-24 10:24:10.364854+0800 TestDemo[18587:9712251] 3------------semaphore 2021-02-24 10:24:10.562344+0800 TestDemo[18587:9712251] 4------------semaphore 2021-02-24 10:24:10.767919+0800 TestDemo[18587:9712251] 5------------semaphore 2021-02-24 10:24:10.987184+0800 TestDemo[18587:9712527] 6------------semaphore 2021-02-24 10:24:11.221210+0800 TestDemo[18587:9712183] 7------------semaphore 2021-02-24 10:24:11.464752+0800 TestDemo[18587:9712527] 8------------semaphore 2021-02-24 10:24:11.690382+0800 TestDemo[18587:9712369] 9------------semaphore 2021-02-24 10:24:15.755117+0800 TestDemo[18587:9711955] end----------( "0:https://blog.csdn.net/weixin_38633659", "1:https://blog.csdn.net/weixin_38633659", "2:https://blog.csdn.net/weixin_38633659", "3:https://blog.csdn.net/weixin_38633659", "4:https://blog.csdn.net/weixin_38633659", "5:https://blog.csdn.net/weixin_38633659", "6:https://blog.csdn.net/weixin_38633659", "7:https://blog.csdn.net/weixin_38633659", "8:https://blog.csdn.net/weixin_38633659", "9:https://blog.csdn.net/weixin_38633659" )
分析:
end打印结果也是在最后的,而且协议请求插入顺序与结果返回顺序是串行的。
适用场景:
资源上传:图片数组资源返回的链接与上传有严格的顺序关系。
原理:
代码中我们只添加了3行代码
dispatch_semaphore_t sem = dispatch_semaphore_create(0); dispatch_semaphore_signal(sem); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_create 创建一个semaphore
dispatch_semaphore_signal 信号量+1,如果前一个值小于0,这个函数在返回之前唤醒一个等待的线程。(发送一个信号 )
dispatch_semaphore_wait 信号量-1。如果结果值小于0,则该函数在返回之前等待一个信号。(等待信号)
如何在GCD中快速的控制并发呢?
信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。
当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
2.4、GCD信号量实现GCD队列组功能(不推荐)
这里还有一种情况,还是使用信号量semaphore,轮询计数,等待全部回调完成之后,增加一个信号量标记。可以实现队列组dispatch_group_t一样的效果。
/// semaphore:等待所有异步请求完成之后,再添加处理信号量 - (void)btn4Action { NSString *str = @"https://blog.csdn.net/weixin_38633659"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; dispatch_semaphore_t sem = dispatch_semaphore_create(0); __block NSInteger count = 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"); }); }
我们可以知道有这个用法,但是不推荐使用这种方法,当需要串行返回的使用信号量实现,而可以并列实现的使用队列组实现。养成一个良好的习惯。
额外拓展:
顺便看一下下面的拓展,也是个很有意思的知识点:
3、iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写):https://www.cnblogs.com/ziyi–caolu/p/4900650.html
https://blog.csdn.net/lfdanding/article/details/100024022