异步地从几个pffile下载数据

时间:2014-03-08 05:15:24

标签: ios objective-c parse-platform downloading

如果我有一组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);    
}];

1 个答案:

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