通过数组循环下载图像时的内存问题
Memory Issues when Downloading Images Looping Through Array
我正在使用以下方法下载数组中艺术家的图像。我用 LastFm to get the images, then store them in 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
。
当它循环遍历此数组时,我还能如何减少内存使用量?任何帮助将非常感激。谢谢。
任何需要一次将所有图像加载到内存中的模式都可能导致问题。将它们直接流式传输到持久存储。然后,根据 UI 的需要,将它们加载到图像视图(和缓存)中。
不清楚 UI 是什么(即是否所有图像视图当前都可见,或者您是否正在填充滚动视图或类似内容)。仅将需要在任何给定时间可见的图像加载到内存中。如果您使用集合视图或 table 视图,这很简单。如果您手动填充滚动视图,那么您将必须完成一些工作来确定哪些是可见的,将它们作为滚动屏幕卸载等。
确保缓存为 UIApplicationDidReceiveMemoryWarningNotification
注册并在内存压力时清空缓存。缓存应该简单地用作优化 UI 速度的机制,但在内存压力下,缓存应该自行清除并且 UI 应该从持久存储中检索图像。所以 UI 应该查看它是否在缓存中,如果是,则使用它。如果不是,请查看它是否在持久存储中,如果是,则使用它(在此过程中将其加载到缓存中)。只有在缓存或持久存储中都找不到它时,它才会从网络中检索它。
图像的分辨率是否高于 UI 要求的分辨率?例如,如果您的按钮为 44 x 44 pt,屏幕比例为 2,则将图像调整为 88 x 88 像素。如果您 resize them 屏幕分辨率图像,它可以显着减少内存影响。
顺便说一下,一旦你解决了这些内存问题,你就可以重新评估1
的maxConcurrentOperationCount
。这会带来严重的性能损失。一般来说,我建议 4 或 5。但在解决内存问题之前不要解决这个问题。
话虽如此,如果您正在进行异步下载,我希望您知道这违背了 maxConcurrentOperationCount
或 1
的意图。如果你想限制并发度,你必须将这些操作包装在异步 NSOperation
子类中(并且在异步下载完成后才完成操作的实现)。
我正在使用以下方法下载数组中艺术家的图像。我用 LastFm to get the images, then store them in 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
。
当它循环遍历此数组时,我还能如何减少内存使用量?任何帮助将非常感激。谢谢。
任何需要一次将所有图像加载到内存中的模式都可能导致问题。将它们直接流式传输到持久存储。然后,根据 UI 的需要,将它们加载到图像视图(和缓存)中。
不清楚 UI 是什么(即是否所有图像视图当前都可见,或者您是否正在填充滚动视图或类似内容)。仅将需要在任何给定时间可见的图像加载到内存中。如果您使用集合视图或 table 视图,这很简单。如果您手动填充滚动视图,那么您将必须完成一些工作来确定哪些是可见的,将它们作为滚动屏幕卸载等。
确保缓存为
UIApplicationDidReceiveMemoryWarningNotification
注册并在内存压力时清空缓存。缓存应该简单地用作优化 UI 速度的机制,但在内存压力下,缓存应该自行清除并且 UI 应该从持久存储中检索图像。所以 UI 应该查看它是否在缓存中,如果是,则使用它。如果不是,请查看它是否在持久存储中,如果是,则使用它(在此过程中将其加载到缓存中)。只有在缓存或持久存储中都找不到它时,它才会从网络中检索它。图像的分辨率是否高于 UI 要求的分辨率?例如,如果您的按钮为 44 x 44 pt,屏幕比例为 2,则将图像调整为 88 x 88 像素。如果您 resize them 屏幕分辨率图像,它可以显着减少内存影响。
顺便说一下,一旦你解决了这些内存问题,你就可以重新评估
1
的maxConcurrentOperationCount
。这会带来严重的性能损失。一般来说,我建议 4 或 5。但在解决内存问题之前不要解决这个问题。话虽如此,如果您正在进行异步下载,我希望您知道这违背了
maxConcurrentOperationCount
或1
的意图。如果你想限制并发度,你必须将这些操作包装在异步NSOperation
子类中(并且在异步下载完成后才完成操作的实现)。