后端 iOS 中图片解码、缓存的时机

quitesilent · January 16, 2020 · Last by handy.wang replied at October 02, 2021 · 99 hits

iOS 图片解码的时机

通过 imageNamed 创建 UIImage 时,系统实际上只是在 Bundle 内查找到文件名,然后把这个文件名放到 UIImage 里返回,并没有进行实际的文件读取和解码。当 UIImage 第一次显示到屏幕上时,其内部的解码方法才会被调用,同时解码结果会保存到一个全局缓存去。据我观察,在图片解码后,App 第一次退到后台和收到内存警告时,该图片的缓存才会被清空,其他情况下缓存会一直存在。

最准确的结论:

手动调用 CGImageSourceCreateWithData 自己绘制图片,将图片绘制到一个 Bitmap 上,这个过程是立即解码的,而且是线程安全的。 其他 UIImage 这一层的接口,都是线程不安全的(iOS9 以后被官方修改成线程安全的了),而且不管是 imageNamed 还是 initWithData,其实都是在图片被首次加载到页面上的时候,才会被解码的。 图片的解码可以放后台线程,渲染不能。

关于 Cache

其实针对图片解码和缓存的逻辑,都可以从 ImageIO 里面找到解答。CGImageSource.h 文件里面的注释已经说的很明白了,在生成 UIImage 的时候,有两个重要的枚举量控制了缓存的时机。

/* Specifies whether the image should be cached in a decoded form. The
 * value of this key must be a CFBooleanRef.
 * kCFBooleanFalse indicates no caching, kCFBooleanTrue indicates caching.
 * For 64-bit architectures, the default is kCFBooleanTrue, for 32-bit the default is kCFBooleanFalse.
 */

IMAGEIO_EXTERN const CFStringRef kCGImageSourceShouldCache;

/* Specifies whether image decoding and caching should happen at image creation time.
 * The value of this key must be a CFBooleanRef. The default value is kCFBooleanFalse (image decoding will
 * happen at rendering time).
 */
IMAGEIO_EXTERN const CFStringRef kCGImageSourceShouldCacheImmediately;

其中 kCGImageSourceShouldCacheImmediately 这个枚举默认是 false,会导致图片的解码和缓存都是在图片第一次被渲染的时候才执行的。 kCGImageSourceShouldCache 这个枚举在 64 位架构(截止到目前市面上的主流 iPhone 机型)的机器上默认是 true。导致其实生成 UIImage 的时候,解码完成的时候都会缓存。

解释下 imageName 和 initWithContentOfFile 的缓存之谜

网上有些博客说 imageName 是缓存图片,initWithContentOfFile 不会缓存图片。其实不然,initWithContentOfFile 其实也是缓存了图片的,不同之处在于这个方法在缓存图片的时候会给图片打一个可消除的标记,保证对应的图片资源被释放的时候,对应的缓存也会被释放。而 imageName 这种方式,只会在 APP 收到内存警告的时候进行释放。这才是两者真正的不同。

引用下官方文档对于 initWithContentOfFile 方法的 Discussion

This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path.

参考文章

网上很多文章对解码和缓存的解释都不是很明确,而且各种重复的转载,还好有一些干货文章。

iOS 处理图片的一些小 Tips

iOS 中的 imageIO 与 image 解码

纠正一下你在文章最后的结论: imageNamed 的图片缓存也是 purgeable 的。如 SDK API 中的描述所说:The system may purge cached image data at any time to free up memory. Purging occurs only for images that are in the cache but are not currently being used.

You need to Sign in before reply, if you don't have an account, please Sign up first.