下载多个文件时如何避免内存警告

时间:2013-11-27 06:52:54

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

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

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的情况下运行它,但它仍然在最后发出内存警告并关闭。可能是什么主要原因以及如何解决它?

3 个答案:

答案 0 :(得分:2)

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

可以提供帮助的一件事是在块中包含@autorelease,例如参见 Using ARC, is it fatal not to have an autorelease pool for every thread?

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

他们将创建自动释放池,并提供一种限制并发下载次数,添加依赖项以及执行其他不需要重新实现的内容的方法。

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

答案 1 :(得分:2)

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

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

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

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:最不适合加载远程资源。 ;)

答案 2 :(得分:1)

从外观上看,您需要限制并发下载次数 积分应该放在这里: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);