代码之家  ›  专栏  ›  技术社区  ›  Rog

消除名为:FUD的UIImage图像

  •  117
  • Rog  · 技术社区  · 17 年前

    2014年2月编辑: 请注意,这个问题源于iOS 2.0! 从那时起,图像要求和处理有了很大的进步。视网膜使图像变得更大,而加载图像则稍微复杂一些。内置对iPad和视网膜图像的支持, 您当然应该在代码中使用ImageNamed .

    我看到很多人在说 imageNamed UITableView s看见 this SO question 例如或 this article

    UIImage 图像命名 方法用于泄漏,所以最好避免,但在最近的版本中已修复。我想更好地理解缓存算法,以便做出合理的决定,确定我可以信任系统在哪里缓存我的图像,以及我需要在哪里多走一段路,自己去做。我目前的基本理解是,这是一个简单的问题 NSMutableDictionary UIImages

    例如,是否有人确定图像缓存在 图像命名 不响应 didReceiveMemoryWarning

    2 回复  |  直到 9 年前
        1
  •  85
  •   Community Mohan Dere    6 年前

    tldr:ImagedNamed很好。它能很好地处理内存。使用它,停止担忧。

    2012年11月编辑 :请注意,此问题源于iOS 2.0!从那时起,图像要求和处理有了很大的进步。视网膜使图像变得更大,而加载图像则稍微复杂一些。由于内置了对iPad和视网膜图像的支持,您当然应该在代码中使用ImageNamed。现在,为了子孙后代:

    这个 sister thread Rincewind 增加了一些权威。

    iPhone OS 2.x中存在这样的问题,即ImageName:缓存即使在出现内存警告后也无法清除。同时,+imageNamed:得到了大量的使用,不是为了缓存,而是为了方便,这可能比它应该得到的更多地放大了问题。

    同时警告

    在速度方面,人们对正在发生的事情普遍存在误解。+ImageName:所做的最大的事情是解码源文件中的图像数据,这几乎总是会显著增大数据大小(例如,屏幕大小的PNG文件在压缩时可能会消耗几十KBs,但解压后会消耗超过半MB的数据-宽度*高度*4)。与此相反,+imageWithContentsOfFile:将在每次需要图像数据时解压缩该图像。正如您所想象的,如果您只需要一次图像数据,那么您在这里什么也得不到,除了有一个缓存版本的图像挂起,并且可能比您需要的时间更长。但是,如果您确实有一个需要经常重画的大图像,那么还有其他选择,尽管我建议的主要选择是避免重画该大图像:)。

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

    我的理解是+imageNamed:cache应该尊重iPhone OS 3.0上的内存警告。当你有机会的时候测试它,如果你发现情况并非如此,就报告错误。

    好了,就这样。ImageName:不会打碎你的窗户或谋杀你的孩子。它非常简单,但它是一个优化工具。可悲的是,它的名字很糟糕,而且没有一个等价物那么容易使用——因此,人们过度使用它,当它仅仅完成它的工作时就会感到不安

    我在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还包括一些示例代码,用于构建您自己的优化版本。我看不出它值得维护,但这里是为了完整性。

    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);
    

        2
  •  5
  •   Community Mohan Dere    9 年前

    根据我的经验,imageNamed创建的映像缓存不会响应内存警告。我有两个应用程序,它们在mem管理方面尽可能地精简,但由于缺少mem,它们仍然莫名其妙地崩溃。当我停止使用imageNamed加载图像时,两个应用程序都变得非常稳定。

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

    StackOverflow question about UIImage cacheing

    另一个注意事项是InterfaceBuilder在封面下使用ImageName。如果你遇到这个问题,一定要记住一些东西。