美文网首页计算机技术一锅炖经验demoiOS实践
GCD实现多个异步线程同步刷新UI

GCD实现多个异步线程同步刷新UI

作者: asdfeng | 来源:发表于2016-12-22 21:30 被阅读1210次

    我们在开发APP的时候可能都会遇到在一个页面请求多个接口的情况,如果在每个接口请求成功后都刷新UI可能会出现闪屏的情况,也可能出现由于数据源不完整导致刷新UI时程序崩溃。那么该如何保证这多个请求之间异步执行且只有在所有请求都成功返回后再进行UI刷新呢?在网上搜索了很多例子基本都是用dispatch_group_asyncdispatch_group_tdispatch_group_notify 组合来实现的。这种方式只是做对了一半,其实现如下:

    dispatch_group_t  group = dispatch_group_create();
    dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_async(group, queue, ^{
            NSLog(@"1");
        });
    dispatch_group_async(group, queue, ^{
            NSLog(@"2");
        });
    dispatch_group_async(group, queue, ^{
           NSLog(@"3");
        });
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"4");
        });
    

    程序运行之后1、2、3先异步执行,然后1、2、3都返回后 4 再最后执行。到这里看似满足了 所有请求都返回后再刷新UI 的要求,但是当我按照这个思路来实现多个网络请求时,发现1、2、3、4的顺序却完全打乱了,4并不总是在最后执行。经过多次调试后才发现原因是因为上面每一个NSLog(...)本身都是同步执行的,而我想要的网路请求本身却是个异步操作。如下

    dispatch_group_async(group, queue, ^{
           [[SFAPIManager sharedManager] requestDataWithPath:urlPath params:@{@"id":@"1"} completeBlock:^(id result, NSError *error) {
            NSLog(@"-------%@",result);
     }];
     });
    

    这会导致在网络请求未回来之前block就已经提前返回了,所以以上代码实现是错误的。正确的方法应该是以上三个函数再配合dispatch_group_enter(group)dispatch_group_leave(group)两个函数一起来使用,这样才能实现我们想要的最终效果。

    dispatch_group_enter(dispatch_group_t group);
    参数group不能为空,在异步任务开始前调用。它明确的表明了一个 block 被加入到了队列组group中,此时group中的任务的引用计数会加1(类似于OC的内存管理)dispatch_group_enter(group)必须与dispatch_group_leave(group)配对使用,它们可以在使用dispatch_group_async时帮助你合理的管理队列组中任务的引用计数的增加与减少。

    dispatch_group_leave(dispatch_group_t group);
    参数group不能为空,在异步任务成功返回后调用。它明确的表明了队列组里的一个 block 已经执行完成,队列组中的任务的引用计数会减1,它必须与dispatch_group_enter(group)配对使用,dispatch_group_leave(group)的调用次数不能多于dispatch_group_enter(group)的调用次数。

    当队列组里的任务的引用计数等于0时,会调用dispatch_group_notify函数。具体代码实现如下:

    @property (nonatomic,strong) dispatch_group_t disGroup;
    
    - (void)requestDatas {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);
        
        dispatch_group_async(self.disGroup, queue, ^{
            [self requestHomeWorks];
        });
        
        dispatch_group_async(self.disGroup, queue, ^{
            [self requestHomeBanner];
        });
        
        dispatch_group_async(self.disGroup, queue, ^{
            [self requestHomeAdvInfos];
        });
           
        dispatch_group_notify(self.disGroup, dispatch_get_main_queue(), ^{
            NSLog(@"4");
            [self.collectionView reloadData];
        });
    }
    
    
    - (void)requestHomeWorks {
        [[SFAPIManager sharedManager] requestDataWithPath:kHomeWorksPath params:nil completeBlock:^(id result, NSError *error) {
            NSLog(@"1");
            dispatch_group_leave(self.disGroup);
        }];
    }
    
    - (void) requestHomeBanner {
        [[SFAPIManager sharedManager] requestDataWithPath:kHomeBannerPath params:nil completeBlock:^(id result, NSError *error) {
            NSLog(@"2");
            dispatch_group_leave(self.disGroup);
        }];
    }
    
    - (void) requestHomeAdvInfos {
        [[SFAPIManager sharedManager] requestDataWithPath:kHomeAdvInfosPath params:nil completeBlock:^(id result, NSError *error) {
            NSLog(@"3");
            dispatch_group_leave(self.disGroup);
        }];
    }
    

    这样就可以实现多个网络请求之间异步执行,且只有在所有请求都返回后才会执行UI刷新的效果。

    相关文章

      网友评论

      • HJD:为什么我按照你的写,这后一步不走呢
        self.disGroup = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_queue_t mainqueue = dispatch_get_main_queue();
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"1");
        });

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"2");
        });

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"3");
        });

        dispatch_group_notify(self.disGroup, mainqueue, ^{
        NSLog(@"4");
        NSLog(@"完成");
        });
        asdfeng:不好意思最近比较忙,刚看到你的评论。你只enter了却没有leave,这么写才对。
        self.disGroup = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_queue_t mainqueue = dispatch_get_main_queue();
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);
        dispatch_group_enter(self.disGroup);

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"1");
        dispatch_group_leave(self.disGroup);
        });

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"2");
        dispatch_group_leave(self.disGroup);
        });

        dispatch_group_async(self.disGroup, queue, ^{
        NSLog(@"3");
        dispatch_group_leave(self.disGroup);
        });

        dispatch_group_notify(self.disGroup, mainqueue, ^{
        NSLog(@"4");
        NSLog(@"完成");
        });
      • 标准答案:楼主, 为什么, 我按照你的方法, 打印的顺序还是随机的, 但是最后打印的都是4
        asdfeng:@标准答案 对啊,就是前面的所有操作随机执行,然后4最后执行。我要实现的效果就是异步执行所有网络请求,待所有的请求都回来后最后刷新UI
      • 今天中午吃什么:也可以用RAC来监听吧,只是一个思路…:relaxed:
        asdfeng:@HJD 可以的
        HJD:我一个页面要加载10个网络请求,用这个可以吗
        asdfeng:嗯这个应该有多种实现方法
      • 6b69124ff056:先mark 接下来的需求里应该会用到 :-D
        asdfeng:@0xDatou 😁会用到的
      • Auditore:/创建信号量/
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        /创建全局并行/
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();

        dispatch_group_async(group, queue, ^{
        NSLog(@"处理事件A");
        for (int i = 0; i<10000; i++) {
        NSLog(@"打印i %d",i);
        }
        dispatch_semaphore_signal(semaphore);
        });
        dispatch_group_async(group, queue, ^{
        NSLog(@"处理事件B");
        for (int i = 0; i<10000; i++) {
        NSLog(@"打印j %d",i);
        }
        dispatch_semaphore_signal(semaphore);
        });
        dispatch_group_async(group, queue, ^{
        NSLog(@"处理事件C");
        for (int i = 0; i<10000; i++) {
        NSLog(@"打印k %d",i);
        }
        dispatch_semaphore_signal(semaphore);
        });
        dispatch_group_async(group, queue, ^{
        NSLog(@"处理事件D");
        for (int i = 0; i<10000; i++) {
        NSLog(@"打印l %d",i);
        }
        dispatch_semaphore_signal(semaphore);
        });
        dispatch_group_notify(group, queue, ^{
        /四个请求对应四次信号等待/
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"处理事件E");
        });
        asdfeng:@Auditore 信号量也是可以的
      • Mr卿:dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 3; i ++)
        {
        dispatch_group_enter(group);
        // 任务代码i 假定任务 是异步执行block回调

        // block 回调执行
        dispatch_group_leave(group);
        // block 回调执行
        }
        });
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_main_queue(), ^{
        // 主线程处理
        }); 楼主 看看这种行不
        asdfeng:@薇薇卿 这么写是不可以的。第一个dispatch_async会直接返回的,然后就开始执行第二个dispatch_async了。
      • Mr卿: dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"4");
        });

        这个 dispatch_get_main_queue() 改成queue应该是最后一步执行的吧
        asdfeng:@薇薇卿 是的
      • xiao_A:赞一个
        遇到异步问题可以搜搜js的相关解法,因为那里全是这样的问题.然后做一个语言迁移就好,思路都是一样的. 这个问题我之前也遇到过,直到某一天我无聊写前端的时候才想到类似楼主的解法 :+1:
        asdfeng:@xiao_A 嗯嗯一定要多多学习
      • 小城东风: :smiley: 哈哈哈,很有感触,想到了之前解决这个问题是的情景
        asdfeng:@小城东风 嗯,以前我都是定义多个变量来标志每个请求是否成功返回了,变量都等于1的时候就刷新,否则就继续等待未完成的请求。
      • swjtu_wj:有demo吗 博主
        asdfeng:@swjtu_wj 只有本地的,没有上传
        asdfeng:@swjtu_wj 暂时没有,把文章里的网络请求方法换成你自己的就可以了
      • 25d82c204314:可以
        asdfeng:@林紫一 谢谢

      本文标题:GCD实现多个异步线程同步刷新UI

      本文链接:https://www.haomeiwen.com/subject/rjbnmttx.html