消除UIImage imageNamed:FUD

编辑2014年2月: 请注意,这个问题的date从iOS 2.0! 自那时以来,图像需求和处理已经发生了很多变化。 视网膜使图像更大,加载稍微复杂。 随着内置支持iPad和视网膜图像, 你一定要在你的代码中使用ImageNamed

我看到很多人说imageNamed是坏的,但是同等数量的人说性能是好的 – 尤其是在渲染UITableView的时候。 例如在iPhoneDeveloperTips.com上看到这个问题或这篇文章

UIImageimageNamed方法用于泄漏,所以最好避免它,但在最近的版本中已经修复。 我想更好地理解cachingalgorithm,以便作出合理的决定,让我可以信任系统来caching我的图像,以及我需要多加努力的地方,并自己做。 我目前的基本理解是,它是由文件名引用的UIImages的一个简单的NSMutableDictionary 。 它变得更大,当内存耗尽时,它变得更小。

例如,有没有人知道imageNamed后面的图像caching不响应didReceiveMemoryWarning ? 苹果似乎不会这样做。

如果您对cachingalgorithm有任何了解,请在此处张贴。

tldr:ImagedNamed很好。 它处理好内存。 使用它,不要担心。

编辑2012年11月 :请注意,这个问题的date从iOS 2.0! 自那时以来,图像需求和处理已经发生了很多变化。 视网膜使图像更大,加载稍微复杂。 随着内置支持iPad和视网膜图像,你一定要在你的代码中使用ImageNamed。 现在,为了后代的缘故:

苹果开发者论坛上的姊妹线获得了更好的stream量。 特别是Rincewind增加了一些权限。

在iPhone OS 2.x中,即使在内存警告之后,imageNamed:cache也不会被清除。 同时+ imageNamed:已经得到了很多不是用于caching的使用,但是为了方便起见,这可能已经把问题放大得比本来应该的更多了。

同时警告这一点

在速度方面,对发生的事情有一个普遍的误解。 + imageNamed所做的最大的事情就是解码来自源文件的图像数据,这些数据几乎总是大大增加数据大小(例如,屏幕大小的PNG文件在压缩时可能会消耗几十KB,但会消耗超过半个MB解压缩 – 宽*高* 4)。 相反+ imageWithContentsOfFile:每次需要图像数据时都会解压该图像。 正如你所想象的,如果你只需要一次图像数据,那么在这里你什么都没有赢,除了有一个caching版本的图像悬而未决,可能会比你需要更长的时间。 然而,如果你有一个大的图像,你需要经常重绘,那么有替代品,但我会build议的主要是避免重画那个大的图像:)。

就caching的一般行为而言,它会根据文件名进行caching(所以+ imageNamed:具有相同名称的两个实例应导致对相同caching数据的引用),并且caching将随着请求更多图像而dynamic增长+ imageNamed :. 在iPhone OS 2.xa上的错误可以防止收到内存警告时收缩caching。

我的理解是,+ imageNamed:caching应尊重iPhone OS 3.0上的内存警告。 如果发现情况并非如此,那么在有机会的情况下进行testing并报告错误。

所以你有它。 imageNamed:不会粉碎你的窗户或谋杀你的孩子。 这很简单,但它是一个优化工具。 可悲的是,它的命名很糟糕,并没有像使用易于使用的等同物 – 因此,人们过度使用它,只是简单地工作而感到不安

我向UIImage添加了一个类别来解决这个问题:

 // header omitted // Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style. + (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; { NSString* bundlePath = [[NSBundle mainBundle] bundlePath]; return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]]; } 

Rincewind还包含一些示例代码来构build自己的优化版本。 我看不出这是值得的主要,但在这里是为了完整。

 CGImageRef originalImage = uiImage.CGImage; CFDataRef imageData = CGDataProviderCopyData( CGImageGetDataProvider(originalImage)); CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData); CFRelease(imageData); CGImageRef image = CGImageCreate( CGImageGetWidth(originalImage), CGImageGetHeight(originalImage), CGImageGetBitsPerComponent(originalImage), CGImageGetBitsPerPixel(originalImage), CGImageGetBytesPerRow(originalImage), CGImageGetColorSpace(originalImage), CGImageGetBitmapInfo(originalImage), imageDataProvider, CGImageGetDecode(originalImage), CGImageGetShouldInterpolate(originalImage), CGImageGetRenderingIntent(originalImage)); CGDataProviderRelease(imageDataProvider); UIImage *decompressedImage = [UIImage imageWithCGImage:image]; CGImageRelease(image); 

与这个代码的权衡是解码图像使用更多的内存,但渲染速度更快。

根据我的经验,由imageNamed创build的图像caching不响应内存警告。 我已经有了两个应用程序,只要内存pipe理尽可能精简,但由于缺less内存而仍然莫名其妙地崩溃。 当我停止使用imageNamed加载图像时,这两个应用程序变得非常稳定。

我承认,这两个应用程序加载了一些大的图像,但没有什么是完全不寻常的。 在第一个应用程序中,我完全跳过了caching,因为用户不太可能两次回到同一图像。 第二,我创build了一个非常简单的caching类,只是做你刚才提到的 – 把UIImage保存在一个NSMutableDictionary中,然后在收到内存警告时刷新它的内容。 如果imageNamed:caching这样,那么我不应该看到任何性能升级。 所有这些都运行在2.2 – 我不知道是否有任何3.0的影响。

你可以从我的第一个应用程序在这里find我的其他问题: StackOverflow有关UIImagecaching的问题

另一个注意事项 – InterfaceBuilder在封面下使用imageNamed。 如果你遇到这个问题,请记住一些事情。