jjzjj

ios - 一次异步地从多个 pffile 下载数据

coder 2024-01-17 原文

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

最佳答案

您可能遇到的是,对于同时运行的大量异步请求,系统可能会由于内存压力和网络停顿或其他资源访问耗尽而阻塞(包括中央处理器)。

您可以使用带有“分配”工具的 Instruments 来验证内存压力的发生。

在内部(即在 Parse 库和系统中)可能有一个变量集,它设置可以同时运行的最大网络请求数。尽管如此,在您的 for 循环中,您将所有请求排入队列。

根据您的情况排队请求意味着什么,此过程根本不是免费的。它可能会消耗大量内存。在最坏的情况下,网络请求将被系统排队,但底层网络堆栈仅执行最大数量并发请求。其他入队待定请求卡在那里等待执行,而它们的网络超时已经在运行。这可能会导致取消未决事件,因为它们的超时已过。

最简单的解决方案

好吧,解决上述问题的最明显方法是简单地序列化所有任务。也就是说,它仅在前一个异步任务完成后才启动下一个异步任务(包括完成处理程序中的代码)。可以使用我命名为“异步循环”的异步模式来完成此操作:

“异步循环”是异步的,因此有一个完成处理程序,它会在所有迭代完成时被调用。

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

关于ios - 一次异步地从多个 pffile 下载数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22265390/

有关ios - 一次异步地从多个 pffile 下载数据的更多相关文章

  1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  8. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  9. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  10. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

随机推荐