用Xcode资产目录进行UIImagecaching

我们都知道UIImage的imageNamed:方法的神秘幕后的caching机制。 在苹果的UIImage类参考中说:

在内存不足的情况下,可能会从UIImage对象中清除图像数据以释放系统内存。 此清除行为仅影响由UIImage对象内部存储的图像数据,而不影响对象本身。 当您尝试绘制数据已被清除的映像时,映像对象将自动从其原始文件中重新加载数据。 然而,这个额外的负载步骤可能会导致一个小的性能损失。

事实上,正如文档所build议的那样,图像数据不会被“从UIImage对象中清除,以释放系统内存”。 相反,应用程序会收到内存警告,直到“由于内存压力”而退出。
编辑:在Xcode项目中使用传统的图像文件引用时,UIImagecaching工作正常。 只是当您转换到资产目录时,内存永远不会被释放。

我实现了一个UIScrollView与几个UIImageViews滚动通过一长串图像。 滚动时,下一个图像正在加载并分配给UIImageView的image属性,删除强大的链接到它以前一直持有的UIImage。

由于imageNamed:的caching机制,我很快耗尽内存,而应用程序终止分配大约170 MB的内存。

当然,还有很多有趣的解决scheme来实现自定义caching机制,包括覆盖类别中的imageNamed: class方法。 通常情况下,类方法imageWithContentOfFile:不会caching图像数据,正如苹果开发人员在WWDC 2011上所build议的那样。

这些解决scheme对于普通的图像文件来说工作得很好,尽pipe你必须得到不像我希望的那样优雅的path和文件扩展名。

不过,我正在使用Xcode 5中引入的新资产目录 ,以便根据设备和有效的映像文件存储使用条件加载映像的机制。 截至目前,似乎没有直接的方法来从资产目录加载图像,而不使用imageNamed: ,除非我缺less一个明显的解决scheme。

你们是否已经用资产目录计算出一个UIImagecaching机制?

我想在UIImage上实现一个类似如下的类:

 static NSCache *_cache = nil; @implementation UIImage (Caching) + (UIImage *)cachedImageNamed:(NSString *)name { if (!_cache) _cache = [[NSCache alloc] init]; if (![_cache objectForKey:name]) { UIImage *image = ???; // load image from Asset Catalog without internal caching mechanism [_cache setObject:image forKey:name]; } return [_cache objectForKey:name]; } + (void)emptyCache { [_cache removeAllObjects]; } @end 

如果使用资产目录,那么更好的办法就是更好地控制UIImage的内部caching,并可以在低内存条件下清除图像数据。

感谢您的阅读,我期待您的想法!

更新:caching驱逐罚款(至less从iOS 8.3)。

我遇到了同样的问题(iOS 7.1.1),尽pipe@ Lukas可能是正确的

这个错误很可能不在苹果的caching里面,而是在你的代码中。

因此,我写了一个非常简单的testing应用程序(查看下面的完整源代码),我仍然看到这个问题。 如果您发现有任何问题 ,请告诉我。 我知道这实际上取决于图像的大小。 我只能在iPad视网膜上看到这个问题。

  @interface ViewController () @property (nonatomic, strong) UIImageView *imageView; @property (nonatomic, strong) NSArray *imageArray; @property (nonatomic) NSUInteger counter; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.imageArray = @[@"img1", ... , @"img568"]; self.counter = 0; UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]]; self.imageView = [[UIImageView alloc] initWithImage: image]; [self.view addSubview: self.imageView]; [self performSelector:@selector(loadNextImage) withObject:nil afterDelay:1]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; NSLog(@"WARN: %s", __PRETTY_FUNCTION__); } - (void)loadNextImage{ self.counter++; if (self.counter < [self.imageArray count]) { NSLog(@"INFO: %s - %lu - %@", __PRETTY_FUNCTION__, (unsigned long)self.counter, [self.imageArray objectAtIndex:self.counter]); UIImage *image = [UIImage imageNamed:[self.imageArray objectAtIndex:self.counter]]; self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height); [self.imageView setImage:image]; [self performSelector:@selector(loadNextImage) withObject:nil afterDelay:0.2]; } else { NSLog(@"INFO: %s %@", __PRETTY_FUNCTION__, @"finished"); [self.imageView removeFromSuperview]; } } @end 

就地实施

我写了一些代码,以保持图像资产,但加载与imageWithData:imageWithContentsOfFile : 使用没有imageNamed的xcassets来防止内存问题?