iOS UIImage存储格式、内存使用和编码/解码

9

iOS是如何存储从压缩数据中加载的图片(jpeg2000、png、jpg等)?

示例:[UIImage imageWithData:pngData]

它是将实际编码字节存储在内部并按需解压缩,还是永久性地将其解压缩为原始像素或其他格式?

2个回答

13
我在iPad 2上创建了一个测试应用程序,使用以下三种方法加载了200个384x384像素的jpeg2000图像文件(总大小为117,964,800字节的原始像素):[UIImage imageNamed][UIImage imageWithContentsOfFile:][UIImage imageWithData]。jpeg2000文件集合包含100个纹理,我将其复制到额外的100个文件中,并添加了“copy”后缀,以查看iOS是否进行了任何重复文件检查。下面会详细说明。
测试分两个步骤进行:
1.仅加载图像并将它们存储在数组中。 2.单独的按钮为每个图像创建UIImageView并显示它们。
以下是结果:

[UIImage imageNamed:]

第一步: 内存仅增加了大约所有jpeg2000文件的总和(每个文件约50K),因此内存增加了约5MB。我认为,在此时重复的文件没有被复制,并且某种方式上被iOS合并,否则如果没有重复检查,此时内存将增加10MB。 第二步: 内存显著增加(约200MB),可能是因为图像解码成BGRA格式以准备在UIImageView中显示。在这个阶段似乎没有重复过滤,并且为每个图像分配了单独的原始内存。我不确定为什么,但这比实际原始像素内存使用应该多80MB左右。

[UIImage imageWithContentsOfFile:]

第一步: 内存使用与[UIImage imageNamed:]相同,因此在此阶段进行了重复过滤。 第二步: 内存使用增加到130MB。由于某种原因,这比[UIImage imageNamed:]低70MB。这个数字更接近200个图像的预期原始像素内存量。

[UIImage imageWithData:]

[NSData dataWithContentsOfFile:] 首先使用。

步骤1:内存使用量为15 MB。我认为此处没有进行重复过滤,因为这接近所有jpeg2000数据的总文件大小。

步骤2:内存使用量增加到139 MB。虽然比[UIImage imageWithContentsOfFile:]更多,但差别不大。

概述

iOS似乎会在需要原始像素时才解码一个使用上述三种方法加载的UIImage的压缩数据。

[UIImage imageNamed:]从未释放内存,因为我的所有图像视图都引用了这些图像。如果我交错加载并允许运行循环执行,它将释放非引用图像的内存。其中一个优点是对同一图像的重复调用[UIImage imageNamed:]实质上是免费的。不要将此方法用于除GUI图像之外的任何东西,否则可能会耗尽内存。

[UIImage imageWithContentsOfFile:]在内存使用方面的行为类似于[UIImage imageNamed:],直到需要原始像素时才更有效地使用内存。当UIImage被释放时,该方法还会立即释放内存。多次使用相同文件调用[UIImage imageWithContentsOfFile:]似乎会使用缓存副本,直到所有引用文件的UIImage都被释放。

[UIImage imageWithData:]不进行缓存或重复检查,并且始终创建新图像。

我测试了相同的PNG文件集,步骤1的结果显示对于imageNamedimageWithContentsOfFile内存使用量更少(约为0.5 MB),而imageWithData显示了所有压缩的PNG文件的总和。我的猜测是iOS仅存储对文件的引用,并在解码时不执行其他任何操作。PNG的步骤2结果相同。


有趣的是[UIImage imageNamed:]从未清除其缓存。这是在哪个iOS版本上?我们已经使用imageNamed:作为我们的图像缓存已经相当长一段时间了,但还没有遇到内存问题。 - sobri
iOS 6和7都经过测试,但从未释放内存。 - jjxtra
这是在一个单一的运行循环中完成的吗?我想知道问题是否是因为imageNamed:方法直到收到内存警告才会清空其缓存,而这可能会发生在后续的运行循环中。 - sobri
这只是一个单独的调用。我将为每个调用再次运行它,并使用dispatch async。 - jjxtra
分批加载似乎有所帮助,尽管在屏幕上有很多图像视图时仍会崩溃。我仍然倾向于只为GUI元素使用imageNamed,对于任何大型、临时或用户生成的内容则使用imageWithContentsOfFile。 - jjxtra
有趣!我们的图像缓存依赖于“imageNamed:”并没有出现任何问题,但是考虑到你的结果,我想知道我们是否只是运气好,如果推得太远,它最终会崩溃。 - sobri

0

一旦加载到内存中,UIImage 以未压缩的 RGBA 格式存在。这意味着图像在内存中占用 width x height x 4 字节,其中 widthheight 是像素的图像大小。

UIImage 在某些情况下可能会提供一些优化,但您应该做好以上规划。

更新:

我想到以上内容并不总是正确的。UIImage 具有 CGImage 属性。然后有各种函数,如 CGImageGetBitmapInfoCGImageGetAlphaInfo 等其他相关函数。这些函数的返回值表明图像可以处于许多不同的格式中。有各种32位格式,以及24位和16位格式。 alpha 可以是第一个、最后一个或完全没有。字节顺序可以是大端或小端。


请查看我的更新答案。似乎有许多可能的格式。请参阅我提到的函数的文档。 - rmaddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接