如果我有一组Message
个对象,每个对象都包含PFile
个数据,是否可以通过异步排队来为每条消息下载数据,如下所示:
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];
}];
}
这似乎导致我的应用挂起,即使这应该在后台完成......
使用以下解决方案:
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);
}];
答案 0 :(得分:3)
您可能遇到的问题是,对于并发运行的大量异步请求,系统可能因内存压力以及网络停顿或其他资源访问耗尽而窒息(包括CPU)。
您可以使用带有&#34;分配&#34;的仪器来验证记忆压力的发生。工具。
在内部(即在Parse库和系统中),可能存在一个变量集,用于设置可以并发运行的最大网络请求数。尽管如此,在for
循环中,您将所有请求排入队列。
根据您的情况中排队请求的含义,此程序根本不是免费的。它可能会花费大量内存。在最坏的情况下,网络请求将由系统排队,但底层网络堆栈仅执行最大数量的并发请求。其他排队但待处理的请求挂起并等待执行,而他们的网络超时已在运行。这可能导致取消待处理事件,因为它们的超时已到期。
那么,解决上述问题最明显的方法就是序列化所有任务。也就是说,它只在前一个完成后启动下一个异步任务(包括完成处理程序中的代码)。人们可以使用异步模式来实现这一点,我命名为#34;异步循环&#34;:
&#34;异步循环&#34;是异步的,因此有一个完成处理程序,它在所有迭代完成时被调用。
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);
}];