【发布时间】:2013-11-24 00:11:24
【问题描述】:
我们都知道 UIImage 的imageNamed: 方法神秘的幕后缓存机制。在 Apple 的 UIImage Class Reference 中,它说:
在内存不足的情况下,可能会从 UIImage 对象中清除图像数据以释放系统上的内存。这种清除行为只影响 UIImage 对象内部存储的图像数据,而不影响对象本身。当您尝试绘制其数据已被清除的图像时,图像对象会自动从其原始文件重新加载数据。然而,这个额外的加载步骤可能会导致小的性能损失。
事实上,图像数据不会像文档建议的那样“从 UIImage 对象中清除以释放系统内存”。 相反,应用会收到内存警告,直到“由于内存压力”退出。
编辑:在 Xcode 中使用常规图像文件引用时项目,UIImage 缓存工作正常。只是当您转换到资产目录时,内存永远不会被释放。
我实现了一个带有几个 UIImageView 的 UIScrollView 来滚动一长串图像。滚动时,正在加载下一个图像并将其分配给 UIImageView 的 image 属性,从而删除它之前持有的 UIImage 的强链接。
由于imageNamed: 的缓存机制,我很快耗尽了内存,并且应用程序终止并分配了大约 170 MB 内存。
当然,有很多有趣的解决方案可以实现自定义缓存机制,包括覆盖类别中的imageNamed: 类方法。通常会使用不缓存图像数据的类方法imageWithContentOfFile:,这甚至在 WWDC 2011 上的 Apple 开发人员所建议的那样。
这些解决方案适用于常规图像文件,但您必须获取路径和文件扩展名,这并不像我希望的那样优雅。
不过,我正在使用 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 的内部缓存以及在使用资产目录时在文档中描述的低内存条件下清除图像数据的可能性。
感谢您的阅读,期待您的想法!
【问题讨论】:
-
错误很可能不在 Apple 的 UIImage 缓存中,而是在您的 UIScrollView 代码中。我建议您使用分析器通过搜索保留 UIImage 实例的组件来跟踪内存泄漏。您是否尝试过在没有图像资源的情况下运行您的应用?
-
我的 UIScrollView 代码很简单。它只是调用 [[self.scrollImageViews objectAtIndex:i] setImage:[UIImage imageNamed:[self nameForImageAtIndex:i]]]。这应该删除对上一张图片的强引用,对吗?使用仪器,我没有检测到任何泄漏。 UIImage 是否为您正常工作,在内存不足的情况下释放内存?
-
我似乎不可能直接从硬盘加载图像,因为图像是在应用程序中以某种方式编译的,根据这个问题stackoverflow.com/questions/18968352/…
-
我在带有大背景图像的 iPad 应用程序上也有同样的情况。 ImageNamed 缓存永远不会被清除,因此应用程序崩溃。完全功能失调的 API。
标签: ios objective-c cocoa-touch