我们都知道UIImage imageNamed:
方法的神秘幕后缓存机制。在Apple的UIImage Class Reference中它说:
在内存不足的情况下,可以从UIImage对象中清除图像数据以释放系统上的内存。此清除行为仅影响UIImage对象内部存储的图像数据,而不影响对象本身。当您尝试绘制其数据已被清除的图像时,图像对象会自动从其原始文件重新加载数据。然而,这个额外的负载步骤可能会导致很小的性能损失。
事实上,正如文档所示,图像数据不会“从UIImage对象中清除以释放系统上的内存”。 相反,应用程序会收到内存警告,直到“由于内存压力”退出为止。
编辑:在Xcode项目中使用传统的图像文件引用时,UIImage缓存可以正常工作。只是当您转换到资产目录时,内存永远不会被释放。
我实现了一个带有几个UIImageViews的UIScrollView来滚动浏览一长串图像。滚动时,正在加载下一个图像并将其分配给UIImageView的image
属性,删除之前保存的UIImage的强链接。
由于imageNamed:
的缓存机制,我很快就会耗尽内存,应用终止时分配的内存大约为170 MB。
当然,有很多有趣的解决方案可以实现自定义缓存机制,包括覆盖类别中的imageNamed:
类方法。通常,使用不缓存图像数据的类方法imageWithContentOfFile:
,就像Apple开发人员在WWDC 2011上所建议的那样。
这些解决方案适用于常规图像文件,但您必须获得不像我希望的那样优雅的路径和文件扩展名。
我正在使用Xcode 5中引入的新资产目录,以利用根据设备和高效图像文件存储有条件地加载图像的机制。截至目前,似乎没有直接的方法从资产目录加载图像而不使用imageNamed:
,除非我错过了一个明显的解决方案。
你们是否已经找到了资产目录的UIImage缓存机制?
我想在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
内部缓存以及在低内存条件下清除图像数据的方法。
感谢您的阅读,我期待您的想法!
答案 0 :(得分:4)
更新:缓存逐出罚款(至少从iOS 8.3开始)。
我遇到了同样的问题(iOS 7.1.1),我觉得@Lukas可能是对的
很有可能这个错误不在Apple的内部......缓存中,而是在你的代码中。
因此我写了一个非常简单的测试应用程序(查看下面的完整资源),我仍然看到了这个问题。如果您发现任何错误,请告诉我相关信息。我知道这取决于图像尺寸。我只在iPad Retina上看到这个问题。
@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
:use xcassets without imageNamed to prevent memory problems?