SDWebImage 中的图片解码
为什么图像在显示到屏幕上之前要进行解码
一般我们使用的图像是JPEG/PNG,这些图像数据不是位图,而是是经过编码压缩后的数据,需要线将它解码转成位图数据,然后才能把位图渲染到屏幕上。
当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。
iOS默认是在主线程解码,所以SDWebImage将这个过程放到子线程了。
同时因为位图体积很大,所以磁盘缓存不会直接缓存位图数据,而是编码压缩后的PNG或JPG数据。
图片加载的工作流
概括来说,从磁盘中加载一张图片,并将它显示到屏幕上,中间的主要工作流如下:
-
假设我们使用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;
-
然后将生成的 UIImage 赋值给 UIImageView ;
-
接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;
-
在主线程的下一个 run loop 到来时,Core Animation 提交了这个隐式的 transaction ,这个过程可能会对图片进行 copy 操作,而受图片是否字节对齐等因素的影响,这个 copy 操作可能会涉及以下部分或全部步骤:
-
分配内存缓冲区用于管理文件 IO 和解压缩操作;
-
将文件数据从磁盘读到内存中;
-
将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的 CPU 操作;
-
最后 Core Animation 使用未压缩的位图数据渲染 UIImageView 的图层。
-
在上面的步骤中,我们提到了图片的解压缩是一个非常耗时的 CPU 操作,并且它默认是在主线程中执行的。那么当需要加载的图片比较多时,就会对我们应用的响应性造成严重的影响,尤其是在快速滑动的列表上,这个问题会表现得更加突出。
为什么要手动图片解码
加载优化:
解压是耗时的,而系统默认是在主线程执行,所以业界通常有一种做法是,异步强制解压,也就是在异步线程主动将二进制图片数据解压成位图数据,使用CGBitmapContextCreate(...)系列方法就能实现。
该处理方式在众多图片处理框架下都有体现。