ios - 通过数组循环下载图像时的内存问题

标签 ios objective-c iphone memory-management automatic-ref-counting

我正在使用以下方法下载数组中艺术家的图像。我使用 LastFm 获取图像,然后将它们存储在 ESCache 中。

self.imageDownloadingQueue = [[NSOperationQueue alloc] init];
self.imageDownloadingQueue.maxConcurrentOperationCount = 1;

self.artworkCache = [[ESCache alloc] initWithName:@"artworkCache" error:nil];

NSArray *artists = // get artists using relevant framework;

for (MPMediaItemCollection *collection in artists){

    [self.imageDownloadingQueue addOperationWithBlock:^{
        @autoreleasepool {

            MPMediaItem *item = [collection representativeItem];
            NSString *artistString = [item valueForProperty:MPMediaItemPropertyAlbumArtist];

            BOOL isCachedImage =   [self.artworkCache objectExistsForKey:artistString];;

            if (!isCachedImage){

                [[LastFm sharedInstance] getInfoForArtist:artistString successHandler:^(NSDictionary *result) {

                    if ([artistString isEqualToString:[[result objectForKey:@"_params"] objectForKey:@"artist"]]) {

                        NSURL *imageURL = [result objectForKey:@"image"];

                        if (imageURL){

                            NSData * data = [NSData dataWithContentsOfURL:imageURL];
                            UIImage *artistImage = [UIImage imageWithData:data];

                            [self.artworkCache setObject:artistImage forKey:artistString];

                            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                [downloadButton setTitle:artistString forState:UIControlStateNormal];
                                [downloadButton setBackgroundImage:artistImage forState:UIControlStateNormal];
                            }];
                            artistImage = nil;
                        }
                        else{
                            UIImage *newArtworkImage = // get placeholder image;

                            if (newArtworkImage != nil){

                                [self.artworkCache setObject:newArtworkImage forKey:artistString];

                                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                    [downloadButton setTitle:artistString forState:UIControlStateNormal];
                                    [downloadButton setBackgroundImage:newArtworkImage forState:UIControlStateNormal];
                                }];
                                newArtworkImage = nil;
                            }
                        }

                    }
                    else{


                    }
                } failureHandler:^(NSError *error) {

                }];
            }
        }
    }];
}

我看到我在 Xcode 中的内存使用量增加到刚刚超过 200mb,然后我的应用程序崩溃并出现以下错误:

2015-01-18 20:56:36.479 Mezzo[1481:39314] Received memory warning. Mezzo(1481,0x105254000) malloc: * mach_vm_map(size=147456) failed (error code=3) * error: can't allocate region *** set a breakpoint in malloc_error_break to debug

它遍历前 50 位左右的艺术家,获取他们的图像(我可以看到 downloadButton 发生变化)但随后崩溃。我不确定如何处理不断增加的内存使用问题。在存储图像后,我将图像设置为 nil,并将所有内容放入 autoreleasepool

当它循环遍历此数组时,我还能如何减少内存使用量?任何帮助将非常感激。谢谢。

最佳答案

  1. 任何需要一次将所有图像加载到内存中的模式都可能会导致问题。将它们直接流式传输到持久存储。然后,根据 UI 的需要,将它们加载到 ImageView (和缓存)中。

  2. 不清楚用户界面是什么(即当前是否所有 ImageView 都可见,或者您是否正在填充 ScrollView 或类似内容)。仅将需要在任何给定时间可见的图像加载到内存中。如果您使用 Collection View 或 TableView ,这将非常简单。如果您手动填充 ScrollView ,那么您将必须完成一些工作来确定哪些是可见的,将它们卸载为滚动屏幕等。

  3. 确保缓存为 UIApplicationDidReceiveMemoryWarningNotification 注册并在内存压力时清空缓存。缓存应该简单地用作优化 UI 速度的机制,但在内存压力下,缓存应该自行清除并且 UI 应该从持久存储中检索图像。所以 UI 应该查看它是否在缓存中,如果是,则使用它。如果不是,请查看它是否在持久存储中,如果是,则使用它(在此过程中将其加载到缓存中)。只有在缓存或持久存储中都找不到它时,它才会从网络中检索它。

  4. 图像的分辨率是否高于 UI 要求的分辨率?例如,如果您的按钮为 44 x 44 pt,屏幕比例为 2,则将图像调整为 88 x 88 像素。如果你resize them对于屏幕分辨率的图像,它可以显着减少内存影响。

  5. 顺便说一下,一旦您解决了这些内存问题,您就可以重新评估 1maxConcurrentOperationCount。这会带来严重的性能损失。一般来说,我会建议 4 或 5。但是在解决内存问题之前不要解决这个问题。

    话虽如此,如果您正在进行异步下载,我希望您知道这违背了 1maxConcurrentOperationCount 的意图。如果要限制并发度,则必须将这些操作包装在异步 NSOperation 子类中(并且在异步下载完成之前不完成操作的实现)。

关于ios - 通过数组循环下载图像时的内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28014877/

相关文章:

objective-c - UICollectionView:如何在委托(delegate)方法中设置特定项目后获取项目大小

objective-c - 使用主视图中的数据在 subview 上加载数组

ios - 使用 iTunes 12.7 安装 IPA

objective-c - iOS 使用 navigationController popToRootViewControllerAnimated

iphone - 仅允许将 UITabViewController 中的一个选项卡的方向更改为横向

iphone - 如何使用NSDate从起点测量时间间隔?

ios - 明文 HTTP 被阻止

iPhone/iPad 主动架构?它是什么?为什么它在那里?谁需要它?

ios - Objective-C - 区分 nil 和 'no value'

ios - 将 block 作为参数传递