Kingfisher源碼閱讀(一)

1、前言

Kingfisher Github地址git

Kingfisherswift語言編寫的一款很是受歡迎的圖片加載庫,功能和OC語言編寫的SDWebImage相似。做者貓神是我初入iOS開發到如今都很崇拜的偶像。github


2、Kingfisher的通常使用

imageView.kf.setImage(with: imageURL)
複製代碼

從上面的使用方法能夠看出Kingfisher的使用方法很是簡單,那麼裏面是怎麼實現的呢?swift


3、 主要流程

1. Kingfisher.swift文件中

關鍵點: Kingfisher緩存

不知道你是否對上面使用方法中的kf好奇,我記得我第一次使用的時候,還不是這種寫法。下面來揭開它的神祕面紗:ide

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關聯類型屬性。url

3.在擴展中實現了KingfisherCompatible協議,指定關聯類型爲Kingfisher<Self>,這裏的Self理解爲協議約束,須要遵照KingfisherCompatible協議的類型,例如這裏的就是Imagespa

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?
{
    // 省略 
    // 根據配置從內存獲取或者從磁盤獲取
}
複製代碼

4、後記

這篇這一篇文章主要分析Kingfisher工做的主要流程,細節待後續文章分享。其中還有不少枝葉操做也是頗有意思的。

相關文章
相關標籤/搜索