当多个块数据调用完成时如何通知

How to notify when multiple block data calls have completed

我希望通过块提取一些数据,但我只想 return 一旦检索到所有数据(所有任意数量的调用都已 returned 和没有得到错误)。我已经研究过 RAC 和 dispatch_groups,但还没有完全弄明白这一点。我尝试执行的代码如下所示:

NSMutableArray *arrayToReturn = [NSMutableArray alloc] init];
for (SPTPartialArtist *partialArtist in artistArray) {
    [SPTRequest requestItemFromPartialObject:partialArtist withSession:self.session callback:^(NSError *error, id object) {
            [arrayToReturn addObject:object];
        }];
}
return arrayToReturn

但是,这只是 return 一个空数组,因为循环在任何数据进入之前就结束了。我本质上是在寻找一种方法,仅当块出现时才 return已在循环中的每个项目上调用,因此 return 数组充满了新对象

为什么不创建一个带有块参数的方法?您的代码将如下所示:

- (void)arrayWithBlock:(void(^)(NSMutableArray *array))block
{
    NSMutableArray *arrayToReturn = [NSMutableArray alloc] init];
    for (SPTPartialArtist *partialArtist in artistArray) {
        [SPTRequest requestItemFromPartialObject:partialArtist withSession:self.session callback:^(NSError *error, id object) {
            [arrayToReturn addObject:object];
            if (arrayToReturn.count == artistArray.count) {
                if (block) {
                    block(arrayToReturn);
                }
            }
        }];
    }
}

在控制器中维护一个数组(假设是 completionArray),并同时维护要在控制器中执行数据下载的块的计数(假设是 blockCount)。

每当特定块完成执行时,该块将在 completionArray 中添加 1(表示成功)或 0(表示失败)。

之后区块将检查blockCount是否匹配completionArray.count。如果它们匹配,则表示所有块都已执行完毕。

然后块将通知控制器相同。
希望这对您有所帮助。

既然你用 reactive-cocoa 标记了,让我们看看 RAC 方法。

您的问题是如何将一系列异步操作变成单个同步操作。 这是个坏主意。这是可能的,但它只会阻塞你所在的线程。如果您已经在使用 RAC,您应该使用信号来管理您的异步方法(例如这个),而不是强制它们同步。

但无论如何。

无论哪种方式,您都希望首先用基于信号的 API 包装 requestItemFromPartialObject:withSession:callback:。您可能应该在 SPTRequest 的类别中执行此操作,但为了简洁起见,我会偷懒:

static RACSignal *request(id<SPTPartialObject> partialObject, SPTSession *session) {
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [SPTRequest requestItemFromPartialObject:partialObject withSession:session callback:^(NSError *error, id object) {
            if (error) {
                [subscriber sendError:error];
            } else {
                [subscriber sendNext:object];
                [subscriber sendCompleted];
            }
        }];
        return nil;
    }];
}

然后你真正想做的是收集艺术家的集合,将其转化为信号集合,然后将其转化为结果集合。一旦你有了它,你可以选择将它转换成一个数组(同步方法)或 return 信号(正确的做法)。

首先,我们想把艺术家的排列变成艺术家的信号。它使事情变得更容易。您可以像这样将数组提升为信号:

RACSignal *artistSignal = [artistArray.rac_sequence signal];

然后将一个事物的信号异步转换为另一个事物的信号就像:

RACSignal *results = [artistSignal flattenMap:^RACSignal *(SPTPartialArtist *partialArtist) {
    return request(partialArtist, self.session);
}];

flattenMap 是一个有点难以获得直觉的操作 -- 它就像 map,但它的转换块 return 是 Signal<T> 而不是 T,然后它 "unwraps" 每个信号都会给你一个 Signal<T> 作为结果(而不是像 map 那样的 Signal<Signal<T>>)。

如果你真的想要同步(你不想),你可以(但不应该)使用toArray方法:

return [results toArray];

这将阻塞,直到所有请求都完成,所以如果您要执行此操作,您需要确保您是在主线程之外执行它,否则您的整个应用程序将冻结。

不过,正确的做法是 return 信号本身,并让您的调用者知道此方法是异步的。

此解决方案还将对结果进行正确排序,因此您不会 运行 陷入当前代码和其他一些答案所具有的排序问题(结果数组包含按返回顺序排列的结果,不是要求的订单)。

你真正需要的是一个dispatch_group和一个完成块,如下所示:

-(void)downloadDataForArtists:(NSArray*)artistsArray withCompletion:(void (^)(NSArray* dataArray))completion
{
    NSMutableArray *arrayToReturn = [NSMutableArray alloc] init];
    dispatch_group_t downloadGroup = dispatch_group_create(); 
    for (SPTPartialArtist *partialArtist in artistArray) {
        dispatch_group_enter(downloadGroup);
        [SPTRequest requestItemFromPartialObject:partialArtist withSession:self.session callback:^(NSError *error, id object) {
        [arrayToReturn addObject:object];
        dispatch_group_leave(downloadGroup); 
        }];
    }
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
        if (completion) {
            completion(arrayToReturn);
        }
    });
}

使用dispatch_group是正确的方法。代码可能是这样的:

-(void)requestItemsFromArtists:(NSArray *)artistArray completionBlock:(void(^)(NSArray *))block{
  NSMutableArray *items = [NSMutableArray array];
  dispatch_group_t group = dispatch_group_create();
  for(SPTPartialArtist *artist in artistArray){
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        [SPTRequest requestItemFromPartialObject:artist withSession:self.session callback:^(NSError *error, id object){
            [items addObject:object];
        }];
    });
  }
 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    if(block){
        block(items);
    }
  });
}