iOS性能优化之图片最佳实践

UIImage是用来处理图像数据的高级类, UIImageView 是 UIKit 提供的用于显示 UIImage 的类 。若采用 MVC 模型进行类比, UIImage 可以看作模型对象( Model ), UIImageView 是一个视图( View ) 。它们都肩负着各自的职责:
UIImage负责加载图片内容, UIImageView 负责显示和渲染它 。

iOS性能优化之图片最佳实践

文章插图
 
这看似是一个简单的单向过程,但实际情况却复杂的多,因为渲染是一个连续的过程,而不是一次性事件 。这里还有一个非常关键的隐藏阶段,对衡量 App 性能至关重要,这个阶段被称为解码 。
iOS性能优化之图片最佳实践

文章插图
 
图片解码在讨论解码之前,先了解下缓冲区的概念 。
缓冲区:是一块连续的内存区域,用来表示一系列元素组成的内存,这些元素具有相同的尺寸,并通常具有相同的内部结构 。
图像缓冲区:它是一种特定缓冲区,它保存了某些图像在内存中的表示 。此缓冲区的每个元素,描述了图像中每个像素的颜色和透明度 。因此这个缓冲区在内存中的大小与它包含的图像大小成正比 。
帧缓冲区:它保存了 app 中实际渲染后的输出 。因此,当 app 更新其视图层次结构时, UIKit 将重新渲染 app 的窗口及其所有视图到帧缓冲区中 。帧缓冲区中提供了每个像素的颜色信息,显示硬件降读取这些信息用来点亮显示器上对应的像素 。
iOS性能优化之图片最佳实践

文章插图
 
如果 app 中没有任何改变,则显示硬件会从帧缓冲区中取出上次看到的相同数据 。但是如果改变了视图内容,UIKit会重新渲染内容,并将其放入帧缓冲区,下一次显示硬件从帧缓冲区读取内容时,就会获取到新的内容 。
数据缓冲区:包含图像文件的数据缓冲区,通常以某些元数据开头,这些元数据描述了存储在数据缓冲区中的图像大小和图像数据本身 。
下面看下图像渲染到帧缓冲区的详细过程:
iOS性能优化之图片最佳实践

文章插图
 
这块区域将由图像视图进行渲染填充 。我们已经为图像视图分配一个 UIImage,它有一个表示图像文件内容的数据缓冲区 。我们需要用每个像素的数据来填充帧缓冲区,为了做到这一点,UIImage 将分配一个图像缓冲区,其大小等于包含在数据缓冲区中的图像大小,并执行称为解码的操作,这就是将 JPEG 或 PNG 或其它编码的图像数据转换为每个像素的图像信息 。然后取决于我们图像视图的内容模式,当 UIKit 要求图像视图进行渲染时,它会将数据复制到帧缓冲区的过程中对来自图像缓冲区的数据进行复制和缩放 。
解码阶段是 CPU 密集型的,特别是对于大型图像 。因此,不是每次 UIKit 要求图像视图渲染时都执行一次这个过程 。 UIImage 绑定在图像缓冲区上,所以它只执行一次这个过程 。因此,在你的 app 中,对于每个被解码的图像,都可能会持续存在大量的内存分配,这种内存分配与输入的图像大小成正比,而与帧缓冲区中实际渲染的图像视图大小没有必然联系,这会对内存产生相当不利的后果 。
减少 CPU 的使用率我们可以使用一种称为向下采样的技术来实现这一目标 。
我们可以通过这种下采样技术来节省一些内存 。本质上,我们要做的就是捕捉该缩小操作,并将其放入缩略图的对象中,最终达到降低内存的目的,因为我们将有一个较小的解码图像缓冲区 。
iOS性能优化之图片最佳实践

文章插图
 
这样,我们设置了一个图像源,创建了一个缩略图,然后将解码缓冲区捕获到 UIImage 中,并将该 UIImage 分配给我们的图像视图 。接下来我们就可以丢弃包含图片数据的数据缓冲区,最终结果就是我们的 app 中将具有一个更小的长期内存占用足迹 。
下面看下如何使用代码来实现这一过程:
  • 首先,创建一个 CGImageSource 对象
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionarylet imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!KCGImageSourceShouldCache 参数为 false,用来告诉 Core Graphic 框架我们只是在创建一个对象,来表示存储在该 URL 的文件中的信息,不要立即解码这个图像,只需要创建一个表示它的对象,我们需要来自此 URL 的文件信息 。


推荐阅读