## 源码阅读系列之图片加载框架: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。
相关推荐
HarderCoder18 小时前
Swift 中的不透明类型与装箱协议类型:概念、区别与实践
swift
HarderCoder18 小时前
Swift 泛型深度指南 ——从“交换两个值”到“通用容器”的代码复用之路
swift
东坡肘子19 小时前
惊险但幸运,两次!| 肘子的 Swift 周报 #0109
人工智能·swiftui·swift
胖虎120 小时前
Swift项目生成Framework流程以及与OC的区别
framework·swift·1024程序员节·swift framework
songgeb1 天前
What Auto Layout Doesn’t Allow
swift
YGGP2 天前
【Swift】LeetCode 240.搜索二维矩阵 II
swift
YGGP2 天前
【Swift】LeetCode 73. 矩阵置零
swift
非专业程序员Ping3 天前
HarfBuzz 实战:五大核心API 实例详解【附iOS/Swift实战示例】
android·ios·swift
Swift社区4 天前
LeetCode 409 - 最长回文串 | Swift 实战题解
算法·leetcode·swift
YGGP7 天前
【Swift】LeetCode 54. 螺旋矩阵
swift