Kingfisher源码阅读(一)

3,212 阅读4分钟

一、前言

Kingfisher Github地址

Kingfisherswift语言编写的一款非常受欢迎的图片加载库,功能和OC语言编写的SDWebImage类似。作者猫神是我初入iOS开发到现在都很崇拜的偶像。


二、Kingfisher的一般使用

imageView.kf.setImage(with: imageURL)

从上面的使用方法可以看出Kingfisher的使用方法非常简单,那么里面是怎么实现的呢?


三、 主要流程

1. Kingfisher.swift文件中

关键点: Kingfisher

不知道你是否对上面使用方法中的kf好奇,我记得我第一次使用的时候,还不是这种写法。下面来揭开它的神秘面纱:

public final class Kingfisher<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

public protocol KingfisherCompatible {
    associatedtype CompatibleType
    var kf: CompatibleType { get }
}

public extension KingfisherCompatible {
    public var kf: Kingfisher<Self> {
        get { return Kingfisher(self) }
    }
}

extension ImageView: KingfisherCompatible { }

这里的Image是为了适配多款系统而typealias的一个类型别名,在AppKit中为NSImage,UIKit中为UIImage

1.先定义了一个不可继承的Kingfisher类,他有一个泛型属性base。

2.然后定义了一个KingfisherCompatible协议,定义了一个只读的kf关联类型属性。

3.在扩展中实现了KingfisherCompatible协议,指定关联类型为Kingfisher<Self>,这里的Self理解为协议约束,需要遵守KingfisherCompatible协议的类型,例如这里的就是Image

4.ImageView遵守KingfisherCompatible协议。

然后就可以使用了,ImageView+Kingfisher.swift中:

extension Kingfisher where Base: ImageView {
    // 省略
}

这里看上去是在给Kingfisher添加扩展,其实是给ImageView,因为Kingfisher中的base属性其实就是ImageView的实例对象,我们只需要在添加的方法中用base代替我们直接给UIImageView添加扩展中的self就行了。

但是目前这种写法有一个限制,那就是结构体无法使用,因为Kingfisher<Self>中的Self是不支持结构体的,如果结构体也想要使用这种方法,那只有单独写,例如String+MD5.swift文件中的:

public struct StringProxy {
    fileprivate let base: String
    init(proxy: String) {
        base = proxy
    }
}

extension String: KingfisherCompatible {
    public typealias CompatibleType = StringProxy
    public var kf: CompatibleType {
        return StringProxy(proxy: self)
    }
}

这里和上面类似就不赘述了,我们自己的写的工具库或者三方也能自定义这样的写法来避免冲突.


2. ImageView+Kingfisher.swift中

关键点: KingfisherOptionsInfo是一个枚举,用来配置库中的信息,例如后台线程解码图片,和出现的动画等等.

这个方法主要配置了一些信息,例如默认图片和加载指示器等等.然后就将获取图片的任务转交给了KingfisherManager。 获取图片成功后,根据配置的显示动画,获取显示并动画显示图片

公开分类方法

public func setImage(with resource: Resource?,
                     placeholder: Placeholder? = nil,
                     options: KingfisherOptionsInfo? = nil,
                     progressBlock: DownloadProgressBlock? = nil,
                     completionHandler: CompletionHandler? = nil) 
                     -> RetrieveImageTask
{
    //  省略 
    //  -> 3 KingfisherOptionsInfo配置后等,将任务交给KingfisherManager。
    //  根据配置的动画,获取显示并动画显示图片
}

3.KingfisherManager.swift中

关键点: KingfisherManager,用来获取图片和下载缓存图片。

获取图片:

public func retrieveImage(with resource: Resource,
        options: KingfisherOptionsInfo?,
        progressBlock: DownloadProgressBlock?,
        completionHandler: CompletionHandler?) -> RetrieveImageTask
{
    //  省略
    // -> 4.1 如果强制刷新,去下载并缓存
    // -> 4.2 从缓存中获取
}

4.1 KingfisherManager.swift中

关键点: ImageDownloader,图片下载器。

关键点: ImageCache,图片缓存器。

关键点: ImageProcessor,图片处理器。

将任务直接交给ImageDownloader,下载成功后用ImageCache来缓存图片,如果有对图片的处理配置,ImageProcessItem还会对图片进行处理。

@discardableResult
func downloadAndCacheImage(with url: URL,
                             forKey key: String,
                      retrieveImageTask: RetrieveImageTask,
                          progressBlock: DownloadProgressBlock?,
                      completionHandler: CompletionHandler?,
                                options: KingfisherOptionsInfo) 
                                -> RetrieveImageDownloadTask?
{
    // 省略
    // -> 5.1 直接将任务交给了ImageDownloader
}

4.2 KingfisherManager.swift中

直接将任务交给了ImageCache,尝试从缓存中获取图片

  • 按照配置能获取到图片,直接返回
  • 没有配置图片的处理,并且没有获取到图片,回到4.1重新下载
  • 有配置图片的处理,尝试获取图片的原图,如果有,处理并返回,没有回到4.1重新下载.
func tryToRetrieveImageFromCache(forKey key: String,
                                 with url: URL,
                                 retrieveImageTask: RetrieveImageTask,
                                 progressBlock: DownloadProgressBlock?,
                                 completionHandler: CompletionHandler?,
                                 options: KingfisherOptionsInfo)
{
    // 省略
    // 5.2 -> 直接将任务交给了ImageCache
}

5.1.1 ImageDownloader.swift中

关键点: ImageDownloader,图片下载器

下载图片

downloadImage(with url: URL,
                       retrieveImageTask: RetrieveImageTask? = nil,
                       options: KingfisherOptionsInfo? = nil,
                       progressBlock: ImageDownloaderProgressBlock? = nil,
                       completionHandler: ImageDownloaderCompletionHandler? = nil) 
                       -> RetrieveImageDownloadTask?
{
    // 省略
    // -> 完
}

5.1.2 ImageCache.swift中

缓存图片,分为内存缓存和磁盘缓存

open func store(_ image: Image,
                      original: Data? = nil,
                      forKey key: String,
                      processorIdentifier identifier: String = "",
                      cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,
                      toDisk: Bool = true,
                      completionHandler: (() -> Void)? = nil)
{
    // 省略 
    // 内存缓存
    // 磁盘缓存
    // -> 完
}

5.2 ImageCache.swift中

根据配置从内存获取或者从磁盘获取获取图片.

@discardableResult
open func retrieveImage(forKey key: String,
                        options: KingfisherOptionsInfo?,
                        completionHandler: ((Image?, CacheType) -> Void)?) 
                        -> RetrieveImageDiskTask?
{
    // 省略 
    // 根据配置从内存获取或者从磁盘获取
}

四、后记

这篇这一篇文章主要分析Kingfisher工作的主要流程,细节待后续文章分享。其中还有很多枝叶操作也是很有意思的。