【问题标题】:How to avoid memory warning while downloading multiple files下载多个文件时如何避免内存警告
【发布时间】:2013-11-27 06:52:53
【问题描述】:

我正在下载多个这样的文件

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

HUD = [[MBProgressHUD alloc] initWithWindow:self.view.window];
HUD.labelText = @"Downloading";
HUD.mode = MBProgressHUDModeIndeterminate;
[self.view.window addSubview:HUD];
[HUD show:YES];

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);

for (NSString *urlString in URLStrings) {
    dispatch_group_async(group, queue, ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
        UIImage *image = [[UIImage alloc] initWithData:data];
        [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {


        }];
    });
}

dispatch_group_notify(group, queue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        HUD.mode = MBProgressHUDModeText;
        HUD.labelText = @"Success";
        [self performSelector:@selector(hideHUDAndBack) withObject:Nil afterDelay:0.3];
    });
});

dispatch_release(group);

当下载总大小为 20MB 或更大的文件时,它会收到内存警告并关闭。我尝试在没有 gcd 的情况下在主线程上运行它,但它仍然在最后收到内存警告并关闭。主要原因是什么以及如何解决?

【问题讨论】:

  • 我尝试在循环中使用 autoreleasepool。它得到了改进,但仍然被关闭。

标签: ios objective-c grand-central-dispatch


【解决方案1】:

在您的方法中,我认为使用调度库来并行化网络请求没有任何优势。使用多个并发网络请求可以实现的是减少网络延迟的影响。但是,您的简单方法同时引入了内存问题。

在给定的场景中,从远程服务器加载视频,我们可以假设文件很大,因此延迟成为一个小问题。主要因素是带宽。但是,当带宽是限制因素时,一次加载多个视频时,您无法更快地加载视频。

因此,我建议您尝试以下更简单的解决方案:

for (NSString *urlString in URLStrings) {
    @autoreleasepool {
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
        UIImage *image = [[UIImage alloc] initWithData:data];
        [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
            // HUD.infoText = @"Saved asset %@, assetURL";
        }];
    }
}
HUD.mode = MBProgressHUDModeText;
HUD.labelText = @"Download complete";
[self performSelector:@selector(hideHUDAndBack) withObject:Nil afterDelay:0.3];

注意:由于下载资源是顺序和同步的,你应该将这些语句包装成一个块并使用dispatch_async,以便在辅助线程上执行(即不在主线程上)。

您现在应该改进的是您下载视频的方式。方法dataWithContentsOfURL: 最不适合加载远程资源。 ;)

【讨论】:

  • 带宽这件事并不完全正确。如果 URL 指向不同的物理服务器,则单个下载可能不会使用设备可用的所有带宽。无论如何,我很想将循环的内容放在 @autoreleasepool { ... } 块中。
  • 是的!即使是单个服务器也可能能够同时有效地处理多个请求(例如,如果速度受到只能使用单核的视频编码的限制怎么办?)所以我建议并行网络请求,但实际上不是使用调度库。
  • 我尝试使用 AFNetworking 它仍然导致内存警告并关闭。已将代码包含在更新中。
  • @JeremyP iPhone 的 Wifi 最大吞吐量约为 150Mbps,即每秒不到 12 MB。告诉我一个服务器在下载 文件时不能使这个带宽饱和。 LTE 可以更快,但在任何合理的服务器阻塞之前,设备的 SSD 可能首先成为限制因素。此外,ADLS 也可能是限制因素。
  • @JeremP 在循环中使用自动释放池绝对是一种改进,感谢您的提示!
【解决方案2】:

首先,您应该监控 Instruments 中的内存使用情况,找出具体原因。

可能有帮助的一点是在块中包含一个@autorelease,例如参见Using ARC, is it fatal not to have an autorelease pool for every thread?

但最重要的是,当您使用更高级别的框架时,好事会自动发生,例如 NSOperation 类(构建在 GCD 之上)或 AFNetworking 库(本身构建在NSOperation)。

他们将创建自动释放池并提供一种方法来限制并发下载的数量、添加依赖项以及执行其他您不必重新实现的事情。

另请注意,同步方法(如前面提到的dataWithContentsOfURL:)在内存占用方面可能不太合理,因为当它们阻塞线程时,您无法执行任何内存管理。

【讨论】:

    【解决方案3】:

    看起来,你需要限制并发下载的数量。
    学分应该在这里:https://stackoverflow.com/a/16105827/727817

    应用于您的案例,它应该类似于:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    
    dispatch_semaphore_t downloadSema = dispatch_semaphore_create(3);  // number of concurrent downloads - this should be set depending on the size of your images
    
    for (NSString *urlString in URLStrings) {
        dispatch_group_async(group, queue, ^{
            dispatch_semaphore_wait(downloadSema, DISPATCH_TIME_FOREVER);
            NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
            UIImage *image = [[UIImage alloc] initWithData:data];
            [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error) {
    
                dispatch_semaphore_signal(downloadSema);
            }];
        });
    }
    
    // ..
    dispatch_release(downloadSema);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-03
      • 2015-04-18
      • 2015-11-28
      • 2011-09-18
      • 2016-12-25
      • 2021-02-04
      • 2013-10-10
      • 2019-02-21
      相关资源
      最近更新 更多