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

时间:2015-08-12 04:38:27

标签: ios objective-c objective-c-blocks reactive-cocoa

我希望通过块提取一些数据,但是我只想在检索完所有数据后返回(所有的任意数量的调用都已返回并且没有出现错误)。我已经研究过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

然而,这只会返回一个空数组,因为循环在任何数据进入之前完成。我实际上只是在寻找一种方法,只有在循环中的每个项目上调用了块时才返回因此,返回数组中充满了新对象

5 个答案:

答案 0 :(得分:4)

这里你真正需要的是一个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);
        }
    });
}

答案 1 :(得分:2)

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

- (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);
                }
            }
        }];
    }
}

答案 2 :(得分:1)

在控制器中维护一个数组(比如completionArray),并在控制器中维护一个计数(比如说blockCount)块来执行数据下载。

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

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

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

答案 3 :(得分:0)

由于您使用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;
    }];
}

那么你真正要做的就是把艺术家的集合,把它变成一组信号,然后把它变成一组结果。一旦你有了,你可以选择将其转换为数组(同步方法)或返回信号(正确的事情)。

对于初学者,我们希望将艺术家阵容变成艺术家的信号。它使事情变得更容易。您可以将数组提升为如下信号:

RACSignal *artistSignal = [artistArray.rac_sequence signal];

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

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

flattenMap是一个有点难以获得直觉的操作 - 它就像map,但它的转换块返回Signal<T>而不是T,并且然后它“解开”每个信号,结果为Signal<T>,而不是Signal<Signal<T>>,而map就是这样。

如果确实希望将此同步(您没有),那么可以(但不应该)使用toArray方法:< / p>

return [results toArray];

这将阻止所有请求完成,所以如果你这样做,你需要确保你在主线程上执行此操作,否则整个应用程序将冻结。

但是,正确的做法是自己返回信号,让你的调用者知道这个方法是异步的。

此解决方案还将正确排序结果,因此您不会遇到当前代码和其他一些答案所具有的排序问题(结果数组包含它们返回的顺序,而不是请求的顺序)。

答案 4 :(得分:0)

使用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);
    }
  });
}