我希望通过块提取一些数据,但是我只想在检索完所有数据后返回(所有的任意数量的调用都已返回并且没有出现错误)。我已经研究过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
然而,这只会返回一个空数组,因为循环在任何数据进入之前完成。我实际上只是在寻找一种方法,只有在循环中的每个项目上调用了块时才返回因此,返回数组中充满了新对象
答案 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);
}
});
}