ios 关于dispatch_group_t的问题导致网络请求中的两个回调

n3h0vuf2  于 2023-07-01  发布在  iOS
关注(0)|答案(2)|浏览(134)

我使用dispatch_group_t来处理我的三个网络请求,代码如下:

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

[self.permissionData removeAllObjects];
dispatch_group_async(group, queue, ^{
    [self getOneData];
});

[self.bannerData removeAllObjects];
dispatch_group_async(group, queue, ^{
    [self getTwoData];
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    [self.tableView.mj_header endRefreshing];
});

- (void)getOneData {
    [self requestData:^(id  _Nullable datas, NSError * _Nullable error) {
          if (error != nil) {
              [self.tableView.mj_header endRefreshing];
          } else {
              if ([datas isKindOfClass:[NSArray class]]) {
                  self.datas = datas;
              }
              [self.tableView reloadData];
          }
    }];
}

这将导致两个方法getOneDatagetTwoData的回调都执行两次。我不知道为什么他们都去两次,但如果我不使用dispatch_group_t,没有问题。我能问问为什么吗?

dwbf0jvd

dwbf0jvd1#

由于您在提交的块中所做的工作本身是异步的,因此您不能依赖dispatch_group_async来管理组的进入和退出。
您需要在调用每个函数之前调用dispatch_group_enter,并在每个函数的完成处理程序中调用dispatch_group_leave
使用您当前的代码,在提交区块时进入组,在完成提交区块时离开组。
因为requestData是异步的,所以getOneData返回,但是网络操作没有完成。
由于getOneData(可能还有getTwoData)是异步的,所以从异步块调用它没有什么价值。
还有一个bug,getOneData的完成处理程序和dispatch_group_notify都调用了endRefreshing。如果在第一个数据请求中有错误,这将导致“不平衡调用”异常。

8ljdwjyq

8ljdwjyq2#

dispatch_group_async将立即进入组,并在被调用的块返回时离开组。但这些块只是调用启动异步任务的方法,但不知道工作何时完成。实际效果是,调度组将在这两个异步任务一启动就得到提前通知,而不是在异步工作完成时得到通知。
因此,挑战在于如何延迟分派组直到异步工作完成。我们经常会用到completion handler块:

- (void)getOneDataWithCompletion:(void (^ _Nullable)(void))completion {
    [self requestData:^(id _Nullable datas, NSError * _Nullable error) {
          …

          if (completion) {
              completion();
          }
    }];
}

我们也会对第二个API调用做同样的事情。
这样,我们现在可以在进行异步调用之前“进入”组,并且只在完成处理程序块中“离开”组,例如:

dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
[self getOneDataWithCompletion:^{
    dispatch_group_leave(group);
}];

dispatch_group_enter(group);
[self getTwoDataWithCompletion:^{
    dispatch_group_leave(group);
}];

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    …
});

作为一般规则,如果您有一个异步方法,那么谨慎的做法是为它提供完成处理程序块参数,在异步方法完成时调用该参数。如果您认为可能不需要该参数,请将其设置为_Nullable(这就是为什么在调用它之前要检查它是否为null)。

相关问题