GCD全解-dispatch-多个网络请求处理

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓜子三百克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值