## 源码阅读系列之图片加载框架: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。
相关推荐
初级代码游戏16 小时前
iOS开发 SwiftUI 14:ScrollView 滚动视图
ios·swiftui·swift
初级代码游戏18 小时前
iOS开发 SwitftUI 13:提示、弹窗、上下文菜单
ios·swiftui·swift·弹窗·消息框
zhyongrui21 小时前
托盘删除手势与引导体验修复:滚动冲突、画布消失动画、气泡边框
ios·性能优化·swiftui·swift
zhangfeng11331 天前
CSDN星图 支持大模型微调 trl axolotl Unsloth 趋动云 LLaMA-Factory Unsloth ms-swift 模型训练
服务器·人工智能·swift
zhyongrui2 天前
SnipTrip 发热优化实战:从 60Hz 到 30Hz 的性能之旅
ios·swiftui·swift
大熊猫侯佩3 天前
Neo-Cupertino 档案:撕开 Actor 的伪装,回归 Non-Sendable 的暴力美学
swift·observable·actor·concurrency·sendable·nonsendable·data race
2501_915921434 天前
在没有源码的前提下,怎么对 Swift 做混淆,IPA 混淆
android·开发语言·ios·小程序·uni-app·iphone·swift
00后程序员张5 天前
对比 Ipa Guard 与 Swift Shield 在 iOS 应用安全处理中的使用差异
android·开发语言·ios·小程序·uni-app·iphone·swift
大熊猫侯佩5 天前
星际穿越:SwiftUI 如何让 ForEach 遍历异构数据(Heterogeneous)集合
swiftui·swift·遍历·foreach·any·异构集合·heterogeneous
hjs_deeplearning5 天前
认知篇#15:ms-swift微调中gradient_accumulation_steps和warmup_ratio等参数的意义与设置
开发语言·人工智能·机器学习·swift·vlm