## 源码阅读系列之图片加载框架:Kingfisher (第二篇)

前言

在源码解析的第一篇中,我们讲解了 Kingfisher 的最主要的功能-异步加载网络图片的流程,以及如何设计支持同一个类型参数可以根据 URL 类型加载本地以及网络的资源。

这一篇我们接着来根据 Features 继续梳理代码。

Useful image processors and filters provided.

Kingfisher 不仅实现了下载和缓存图片,它也给我们提供了很多 UI 方面很实用的功能。比如 Github 上面举例的 DownsamplingImageProcessor,示例代码如下:

scss 复制代码
let processor = DownsamplingImageProcessor(size: imageView.bounds.size)
             |> RoundCornerImageProcessor(cornerRadius: 20)

这一句代码可以实现下面的两个功能:

  • 对其进行下采样以匹配图像视图大小。
  • 使其具有给定半径的圆角。 其他的 processor 大家可以去 ImageProcessor 这个文件自行查阅,这里就不展开讲了。

Multiple-layer hybrid cache for both memory and disk.

Kingfisher 加载网络图片的时候,支持内存和磁盘两种缓存。如果你不指定,默认情况下是图片资源在内存和磁盘中都会缓存。

内存缓存

代码如下:

swift 复制代码
func storeNoThrow(
            value: T,
            forKey key: String,
            expiration: StorageExpiration? = nil) {
    lock.lock()
    defer { lock.unlock() }
    let expiration = expiration ?? config.expiration
    // The expiration indicates that already expired, no need to store.
    guard !expiration.isExpired else { return }
    
    let object: StorageObject<T>
    if config.keepWhenEnteringBackground {
        object = BackgroundKeepingStorageObject(value, expiration: expiration)
    } else {
        object = StorageObject(value, expiration: expiration)
    }
    storage.setObject(object, forKey: key as NSString, cost: value.cacheCost)
    keys.insert(key)
}

首先是用 NSLock 加锁,因为图片加载是多线程异步加载的,所以需要加锁防止数据竞争。接着是判断缓存的有效期,默认值是 300s,如果在有效期内直接返回。

接着就是缓存的操作了,这里判断了一下 APP 是否已经退入后台,如果退入后台则使用 BackgroundKeepingStorageObject ,若没有则使用 StorageObject

最后,使用 NSCache 数据结构,将下载的图片资源存入内存中。需要注意的是,如果是网络加载的资源,key 默认情况下是 URL 的 absoluteString,如果你想自定义 cacheKey 的话需要使用 ImageResource 来初始化对象,示例代码如下:

ini 复制代码
let url = URL(string: "xxx/images/kingfisher-1.jpg")!
let imageResource = KF.ImageResource(downloadURL: url, cacheKey: "hello")

磁盘缓存

代码如下:

swift 复制代码
// 磁盘缓存
public func store(
            value: T,
            forKey key: String,
            expiration: StorageExpiration? = nil,
            writeOptions: Data.WritingOptions = []) throws {
    guard storageReady else {
        throw KingfisherError.cacheError(reason: .diskStorageIsNotReady(cacheURL: directoryURL))
    }

    let expiration = expiration ?? config.expiration
    // The expiration indicates that already expired, no need to store.
    guard !expiration.isExpired else { return }
    
    let data: Data
    do {
        data = try value.toData()
    } catch {
        。。。
    }

    let fileURL = cacheFileURL(forKey: key)
    do {
        try data.write(to: fileURL, options: writeOptions)
    } catch {
        。。。
    }

    let now = Date()
    let attributes: [FileAttributeKey : Any] = [
        // The last access date.
        .creationDate: now.fileAttributeDate,
        // The estimated expiration date.
        .modificationDate: expiration.estimatedExpirationSinceNow.fileAttributeDate
    ]
    do {
        try config.fileManager.setAttributes(attributes, ofItemAtPath: fileURL.path)
    } catch {
        。。。
    }

    maybeCachedCheckingQueue.async {
        self.maybeCached?.insert(fileURL.lastPathComponent)
    }
}

磁盘缓存首先判断了 storageReady 是否为true,接着判断了是否缓存已过期(磁盘缓存默认时间是 7 天)。

前置条件都符合之后,将图片转为 Data,调用 cacheFileURL 拿到图片的磁盘路径,然后调用 write(to url: URL, options: Data.WritingOptions = []) 将其写入磁盘中。

最后,再将磁盘缓存文件的过期时间更新为当前时间。

Fine control on cache behavior. Customizable expiration date and size limit.

Kingfisher 不仅可以支持内存和磁盘的双重缓存,它也很人性化的提供了很多参数,让我们可以自定义缓存的各种参数。

下面是该库支持的缓存参数:

  • forceRefresh:默认为 false,若为 true 则代表无论有没有缓存都去网络下载。
  • fromMemoryCacheOrRefresh:默认为 false,若为 true 则代表不使用磁盘缓存只使用内存缓存,若内存缓存没有就去网络下载。
  • cacheMemoryOnly:默认为 false,若为 true 则代表不使用磁盘缓存只使用内存缓存。
  • memoryCacheExpiration: 内存缓存过期时间设置,默认为 300秒。
  • diskCacheExpiration:磁盘缓存过期时间设置,默认为 7 天。
  • memoryCacheAccessExtendingExpiration:每次访问内存缓存资源后,该资源的过期时间延长策略。默认是使用原缓存时间。比如 a 图片被首次缓存,默认过期时间是 300 秒。20 秒之后你访问了 a 图片,那 a 图片的过期时间会重置成 300 秒。
  • diskCacheAccessExtendingExpiration:磁盘的过期时间延长策略,默认逻辑行为同 memoryCacheAccessExtendingExpiration。
相关推荐
Keya21 小时前
lipo 命令行指南
ios·xcode·swift
zhangmeng21 小时前
SwiftUI中如何实现子视图向父视图传递数据?
ios·swiftui·swift
Saafo21 小时前
迁移至 Swift Actors
ios·swift
杂雾无尘2 天前
告别构建错误, iOS 开发架构难题全面解析, 避免 CPU 架构陷阱
ios·swift·客户端
大熊猫侯佩3 天前
探秘 WWDC 25 全新 #Playground 宏:提升 Swift 开发效率的超级神器
xcode·swift·wwdc
移动端小伙伴3 天前
10.推送的扩展能力 — 打造安全的通知体验
swift
移动端小伙伴3 天前
推送的扩展能力 — 打造个性化的通知体验
swift
移动端小伙伴3 天前
远程推送(Remote Push Notification)
swift
移动端小伙伴3 天前
本地通知的精准控制三角:时间、位置、情境
swift
移动端小伙伴3 天前
本地通知内容深度解析 — 打造丰富的通知体验
swift