消除UIImage图像命名:FUD

Rog*_*Rog 117 iphone caching uikit uiimageview imagenamed

编辑2014年2月:请注意,这个问题可以追溯到iOS 2.0!从那时起,图像要求和处理已经发生了很大变化.Retina使图像更大,加载它们稍微复杂一些.内置支持iPad和视网膜图像,您当然应该在代码中使用ImageNamed.

我看到很多人都说imageNamed不好,但同样数量的人说性能很好 - 特别是在渲染时UITableView.例如,在iPhoneDeveloperTips.com上查看此SO问题本文

UIImageimageNamed用于泄漏的方法因此最好避免,但在最近的版本中已得到修复.我想更好地理解缓存算法,以便做出合理的决定,我可以信任系统来缓存我的图像,以及我需要加倍努力并自己完成.我目前的基本理解是它是一个简单NSMutableDictionaryUIImages文件名引用.它变得越来越大,当内存耗尽时,它会变得更小.

例如,有没有人确切知道后面的图像缓存imageNamed没有响应didReceiveMemoryWarning?Apple似乎不太可能不这样做.

如果您对缓存算法有任何了解,请在此处发布.

Rog*_*Rog 86

tldr:ImagedNamed很好.它很好地处理内存.使用它并停止担心.

编辑2012年11月:请注意,这个问题可以追溯到iOS 2.0!从那时起,图像要求和处理已经发生了很大变化.Retina使图像更大,加载它们稍微复杂一些.内置支持iPad和视网膜图像,您当然应该在代码中使用ImageNamed.现在,为了后人的缘故:

妹妹线程在苹果开发论坛收到一些更好的交通.特别是Rincewind增加了一些权威.

在iPhone OS 2.x中存在一些问题,即使在发出内存警告之后,imageNamed:cache也不会被清除.同时+ imageNamed:已经得到了很多用于缓存的功能,但为了方便起见,这可能会使问题更加放大.

同时警告

在速度方面,人们普遍误解了发生的事情.+ imageNamed做的最大的事情就是解码源文件中的图像数据,这几乎总是会显着增加数据大小(例如,屏幕大小的PNG文件在压缩时可能会消耗几十KB,但是消耗的半个MB以上解压缩 - 宽度*高度*4).相比之下+ imageWithContentsOfFile:每次需要图像数据时都会解压缩该图像.你可以想象,如果你只需要一次图像数据,那么你在这里什么也没有赢,除了有一个图像的缓存版本,并且可能比你需要的时间更长.但是,如果你有一个需要经常重绘的大图像,那么有其他选择,虽然我建议主要是避免重绘那个大图像:).

关于缓存的一般行为,它基于文件名进行缓存(因此+ imageNamed的两个实例:具有相同的名称应该导致对相同缓存数据的引用)并且当您通过请求更多图像时缓存将动态增长+ imageNamed :. 在iPhone OS 2.xa上,当收到内存警告时,bug会阻止缓存收缩.

我的理解是+ imageNamed:cache应该尊重iPhone OS 3.0上的内存警告.如果您发现不是这种情况,请在有机会时进行测试并报告错误.

所以你有它.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]];
}
Run Code Online (Sandbox Code Playgroud)

Rincewind还包含一些示例代码来构建您自己的优化版本.我看不出它值得主持,但这里是为了完整性.

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);
Run Code Online (Sandbox Code Playgroud)

与此代码的权衡是解码图像使用更多内存但渲染速度更快.


Bde*_*eez 5

根据我的经验,imageNamed创建的图像缓存不响应内存警告.我有两个应用程序,就像我可以得到它们一样精简到内存管理,但由于缺乏内存,仍然莫名其妙地崩溃.当我停止使用imageNamed加载图像时,两个应用程序都变得非常稳定.

我承认这两个应用程序都加载了大量的图像,但没有任何完全不同寻常的版本.在第一个应用程序中,我只是完全跳过缓存,因为用户不太可能两次返回相同的图像.在第二个中,我构建了一个非常简单的缓存类,正如你所提到的那样 - 将UIImages保存在NSMutableDictionary中,然后在收到内存警告时刷新其内容.如果imageNamed:要像那样缓存,那么我不应该看到任何性能升级.所有这一切都在2.2上运行 - 我不知道是否有任何3.0的影响.

你可以在我的第一个应用程序中找到关于这个问题的另一个问题: StackOverflow关于UIImage缓存的问题

另一个注意事项 - InterfaceBuilder使用了图像命名.如果遇到这个问题,请记住一些事情.