asynchronous地同时从几个pffile下载数据

如果我有一个Message对象数组,每个对象都带有一个包含数据的PFile ,那么可以通过asynchronous排队它们来为每个单独的消息下载数据,如下所示:

 for (int i = 0; i < _downloadedMessages.count; i++) { PFObject *tempMessage = (PFObject *)[_downloadedMessages objectAtIndex:i]; [[tempMessage objectForKey:@"audio"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { [self persistNewMessageWithData:data]; }]; } 

这似乎导致我的应用程序挂起 ,即使这应该在后台完成…

使用下面的解决scheme:

 NSMutableArray* Objects = ... [self forEachPFFileInArray:Objects retrieveDataWithCompletion:^BOOL(NSData* data, NSError*error){ if (data) { PFObject *tempObj = (PFObject *)Object[someIndex...]; [self persistNewMessageWithData:data andOtherInformationFromObject:tempObj]; return YES; } else { NSLog(@"Error: %@", error); return NO; // stop iteration, optionally continue anyway } } completion:^(id result){ NSLog(@"Loop finished with result: %@", result); }]; 

您可能遇到的情况是,对于同时运行的大量asynchronous请求,由于内存压力以及networking延迟或其他耗尽资源(包括CPU)的访问,系统可能会窒息。

您可以使用仪器和“分配”工具来validation内存压力的发生。

内部(即在Parse库和系统中)可能有一个variables集,它设置可以并发运行的最大networking请求数。 尽pipe如此,在你的for循环中你排队所有请求。

根据你的情况排队请求意味着什么,这个过程根本不是免费的。 这可能会花费大量的内存。 在最坏的情况下,networking请求将被系统join队列,但底层networking堆栈只执行最大数量并发请求。 其他排队挂起的请求挂在那里等待执行,而他们的networking超时已经在运行。 这可能导致取消挂起的事件,因为它们的超时过期。

最简单的解决scheme

那么,解决上述问题的最明显的方法就是简单地连续所有任务。 也就是说,它只在前一个完成时(包括完成处理程序中的代码)开始下一个asynchronous任务。 我们可以使用asynchronous模式来完成这个工作,我称之为“asynchronous循环”:

“asynchronous循环”是asynchronous的,因此有一个完成处理程序 ,当所有的迭代完成时被调用。

 typedef void (^loop_completion_handler_t)(id result); typedef BOOL (^task_completion_t)(PFObject* object, NSData* data, NSError* error); - (void) forEachObjectInArray:(NSMutableArray*) array retrieveDataWithCompletion:(task_completion_t)taskCompletionHandler completion:(loop_completion_handler_t)completionHandler { // first, check termination condition: if ([array count] == 0) { if (completionHandler) { completionHandler(@"Finished"); } return; } // handle current item: PFObject* object = array[0]; [array removeObjectAtIndex:0]; PFFile* file = [object objectForKey:@"audio"]; if (file==nil) { if (taskCompletionHandler) { NSDictionary* userInfo = @{NSLocalizedFailureReasonErrorKey: @"file object is nil"} NSError* error = [[NSError alloc] initWithDomain:@"RetrieveObject" code:-1 userInfo:userInfo]; if (taskCompletionHandler(object, nil, error)) { // dispatch asynchronously, thus invoking itself is not a recursion dispatch_async(dispatch_get_global(0,0), ^{ [self forEachObjectInArray:array retrieveDataWithCompletion:taskCompletionHandler completionHandler:completionHandler]; }); } else { if (completionHandler) { completionHandler(@"Interuppted"); } } } } else { [file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { BOOL doContinue = YES; if (taskCompletionHandler) { doContinue = taskCompletionHandler(object, data, error); } if (doContinue) { // invoke itself (note this is not a recursion") [self forEachObjectInArray:array retrieveDataWithCompletion:taskCompletionHandler completionHandler:completionHandler]; } else { if (completionHandler) { completionHandler(@"Interuppted"); } } }]; } } 

用法:

 // Create a mutable array NSMutableArray* objects = [_downloadedMessages mutableCopy]; [self forEachObjectInArray:objects retrieveDataWithCompletion:^BOOL(PFObject* object, NSData* data, NSError* error){ if (error == nil) { [self persistNewMessageWithData:data andOtherInformationFromObject:object]; return YES; } else { NSLog(@"Error %@\nfor PFObject %@ with data: %@", error, object, data); return NO; // stop iteration, optionally continue anyway } } completion:^(id result){ NSLog(@"Loop finished with result: %@", result); }];