Kingfisher 缓存序列化全解析:CacheSerializer 协议、DefaultCacheSerializer 与 FormatIndicatedCacheSerializer 实战指南
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
本指南以 Kingfisher 官方文档 CommonTasks_Serializer.md 为主体,系统讲解磁盘缓存序列化的完整机制:从CacheSerializer协议的设计意图,到默认序列化器的内部实现,再到强制指定图片格式、以及编写自定义序列化器接入setImage(with:)的完整流程。阅读完本文,你将掌握如何控制图片落盘格式、如何在圆角裁剪等场景下正确保留透明通道、以及如何通过originalDataUsed影响缓存命中后的再处理行为。
什么是 CacheSerializer:磁盘缓存的双向翻译器
Kingfisher 的内存缓存(MemoryStorage)直接持有解码后的图片对象,而磁盘缓存(DiskStorage)只能写入Data。因此,在"图片 → 磁盘文件"与"磁盘文件 → 图片"这两个方向之间,需要一个负责转换的角色,这就是CacheSerializer协议的职责。
协议定义在 Sources/Cache/CacheSerializer.swift,包含两个核心方法:
public protocol CacheSerializer: Sendable { /// 将图片序列化为 Data,用于写入磁盘缓存 func data(with image: KFCrossPlatformImage, original: Data?) -> Data? /// 将磁盘读出的 Data 反序列化为图片对象 func image(with data: Data, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? /// 是否倾向于直接缓存原始下载数据 var originalDataUsed: Bool { get } }三个要素的语义如下:
data(with:original:):发生在存储阶段。image是经过处理器(Processor)处理后的最终图片;original是网络下载得到的原始字节数据——如果图片来自缓存而非新下载,则original为nil。image(with:options:):发生在磁盘读取阶段,options携带imageCreatingOptions等反序列化所需配置。originalDataUsed:协议扩展提供了默认值false(见 CacheSerializer.swift),表示磁盘上保存的是"处理后的图片";若返回true,则优先缓存原始数据,从磁盘加载后会重新应用处理器得到最终图片。
使用默认序列化器:什么都不用做
绝大多数场景下,你甚至不需要感知序列化器的存在。标准用法与显式指定等价:
// 什么都不传,Kingfisher 自动使用默认序列化器 imageView.kf.setImage(with: url) // 与上面完全等价 imageView.kf.setImage(with: url, options: [.cacheSerializer(DefaultCacheSerializer.default)])默认值来自 KingfisherOptionsInfo.swift 中.cacheSerializer选项的注释说明:"If not set, theDefaultCacheSerializer.defaultwill be used.",具体体现在该文件的parsedOptions默认属性public var cacheSerializer: any CacheSerializer = DefaultCacheSerializer.default(KingfisherOptionsInfo.swift)。
DefaultCacheSerializer原生支持 PNG、JPEG、GIF 三种格式,其判定依据是original数据的文件头(magic bytes),实现在 Sources/Image/ImageFormat.swift:通过比对前 8 个字节识别 PNG 特征头、0xFF 0xD8开头的 JPEG 以及GIF三个字母的 GIF 头。当格式无法识别(.unknown)时,会退化为 PNG 表示(pngRepresentation())。
两个可调属性
DefaultCacheSerializer并非铁板一块,它暴露了两个可配置属性(见 CacheSerializer.swift):
public struct DefaultCacheSerializer: CacheSerializer { /// JPEG 等有损格式的压缩质量,默认 1.0 public var compressionQuality: CGFloat = 1.0 /// 是否优先缓存原始数据,默认 false public var preferCacheOriginalData: Bool = false }compressionQuality:仅在编码为 JPEG 这类有损格式时生效,取值0.0...1.0。preferCacheOriginalData:置为true后,data(with:original:)会直接返回original(非 nil 时),否则才回退为对图片编码。
强制指定格式:FormatIndicatedCacheSerializer
当默认的"跟随原图格式"策略不满足需求时,可以使用FormatIndicatedCacheSerializer,它为所有受支持格式提供了现成的单例:
| 静态成员 | 说明 |
|---|---|
FormatIndicatedCacheSerializer.png | 强制以 PNG 格式序列化 |
FormatIndicatedCacheSerializer.jpeg | 强制以 JPEG 格式序列化,压缩质量固定为 1.0 |
FormatIndicatedCacheSerializer.jpeg(compressionQuality:) | 以指定压缩质量序列化为 JPEG |
FormatIndicatedCacheSerializer.gif | 强制以 GIF 格式序列化 |
其源码定义在 Sources/Cache/FormatIndicatedCacheSerializer.swift。值得注意的是它的降级策略(FormatIndicatedCacheSerializer.swift):首先尝试用指定格式编码;若图片无法表示为该格式(例如强制 GIF 但图片不支持),则回退到original数据本身带有的真实格式;最后才兜底为原图数据的 PNG 表示。
例如某个 PNG 图片用
FormatIndicatedCacheSerializer.jpeg序列化:JPEG 不支持透明通道,若图片含 alpha 通道无法直接转换,就会回退为原 PNG 数据落盘,避免信息丢失。
实战场景:圆角裁剪时强制使用 PNG 序列化器
DefaultCacheSerializer以保留输入数据的原始格式为目标,但某些场景下"忠实还原"反而有害。最典型的例子是配合RoundCornerImageProcessor做圆角裁剪:
- 圆角处理通常需要 alpha 通道来表现四周的透明过渡;
- JPEG 本身不支持 alpha 通道,若圆角图片被存成 JPEG,再次加载时角落区域会被填充为白色;
- 因此应显式指定 PNG 序列化器,保证透明通道在磁盘缓存中得以保留。
let roundCorner = RoundCornerImageProcessor(cornerRadius: 20) imageView.kf.setImage(with: url, options: [.processor(roundCorner), .cacheSerializer(FormatIndicatedCacheSerializer.png)] )在 FormatIndicatedCacheSerializer.swift 的文档示例中,官方给出了更完整的头像场景:对 44×44 的图片做全圆角处理后使用 PNG 序列化器,"The image will always be cached as PNG format to preserve the alpha channel for the round rectangle",并从缓存加载后依然是圆角效果。
这一行为也体现在 Kingfisher 官方 Demo 的实践里——Demo 项目中RoundCornerImageProcessor与序列化器组合的使用可参见 Demo/Demo/Kingfisher-Demo/ViewControllers/ProcessorCollectionViewController.swift。
编写自定义序列化器
当默认实现与格式指示实现都无法满足需求(例如接入私有加密格式、WebP 扩展、服务端定制的位图协议)时,可以让自定义类型遵循CacheSerializer,实现data(with:original:)与image(with:options:)两个方法即可:
struct MyCacheSerializer: CacheSerializer { func data(with image: Image, original: Data?) -> Data? { return MyFramework.data(of: image) } func image(with data: Data, options: KingfisherParsedOptionsInfo?) -> Image? { return MyFramework.createImage(from: data) } }随后通过.cacheSerializer(_:)选项传入setImage(with:):
let serializer = MyCacheSerializer() let url = URL(string: "https://yourdomain.com/example.png") imageView.kf.setImage(with: url, options: [.cacheSerializer(serializer)])需要留意两点实现细节:
- 自定义序列化器同样接收
original参数,可据此判断数据来源(下载 vs 缓存),并决定是否复用原始数据。 - 若实现
originalDataUsed返回true,请确保在image(with:options:)返回的是未处理的图片,由 Kingfisher 在读取后自动套用处理器。
序列化器在缓存管线中的调用位置
从源码可以确认序列化器在两条路径中的确切位置:
- 写入磁盘:ImageCache.swift 中,存储图片到磁盘前调用
serializer.data(with: image, original: original),若返回nil则触发KingfisherError.cacheError(reason: .cannotSerializeImage(...))错误。 - 读取磁盘:ImageCache.swift 中,从
diskStorage读出Data后调用options.cacheSerializer.image(with: data, options: options)重建图片,随后视backgroundDecode选项决定是否再解码。
可见,序列化器直接影响磁盘缓存的存取两端,是整个缓存链路中可插拔的关键扩展点。
深入理解 originalDataUsed:缓存命中后是否再次处理
originalDataUsed直接决定了磁盘缓存的内容形态与后续行为,测试用例 Tests/KingfisherTests/KingfisherManagerTests.swift 用两个对称用例验证了这一点:
- 默认行为(
originalDataUsed == false):磁盘保存的是处理器处理后的图片。第二次读取(cacheType == .disk)时处理器不再执行,因为磁盘上已是成品(对应测试testCouldProcessAgainWhenSerializerCachesOriginalData的反例部分)。 preferCacheOriginalData = true(即originalDataUsed == true):磁盘保存原始下载数据,第二次从磁盘加载后会重新应用处理器(测试断言XCTAssertTrue(p2.processed))。
var s = DefaultCacheSerializer() s.preferCacheOriginalData = true let options: KingfisherOptionsInfo = [.processor(p), .cacheSerializer(s), .waitForCache]这一开关的取舍取决于业务:希望"缓存即最终形态、读取零开销",保持默认;希望"同一份原始数据可被不同处理器复用",则开启原始数据缓存。
小结
CacheSerializer协议是磁盘缓存的数据翻译层,Kingfisher 官方文档 CommonTasks_Serializer.md 提供了从默认到自定义的完整接入路径。- 默认情况下使用
DefaultCacheSerializer.default,自动识别 PNG/JPEG/GIF,无需任何配置。 - 涉及透明通道(圆角、蒙版、贴纸)时,务必使用
FormatIndicatedCacheSerializer.png防止 alpha 信息在 JPEG 落盘时丢失。 - 自定义序列化器只需实现两个方法,即可接入私有格式或第三方编解码框架;善用
originalDataUsed可以精确控制"缓存原始数据 + 读取时再处理"的缓存策略。
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考