Kingfisher 源碼閱讀以及部分小收獲

screenshot.png

Kingfisher

部分收獲:
  1. Kingfisher的 kf 寫法
    同樣是鏈式編程, 在SnapKit中,view.snp是通過對View進行擴展實現的
    類似snp的寫法:
public var snp: ConstraintViewDSL {
        return ConstraintViewDSL(view: self)
    }

這種寫法來為類添加一個不存在的屬性, snp
但是在 Kingfisher中,onev 是使用了泛型結合協議來做這個事情的.

/**
 A type that has Kingfisher extensions.
 */
public protocol KingfisherCompatible {
    associatedtype CompatibleType
    var kf: CompatibleType { get }
}

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

extension ImageView: KingfisherCompatible { }
extension Image: KingfisherCompatible { }

給ImageView和image遵守了一個協議 KingfisherCompatible
從而實現了

 imageView.kf 就等同于 let kf: Kingfisher = Kingfisher<imageView>
  1. 讀寫權限不同
    fileprivate(set)
/// It will be `nil` if `indicatorType` is `.none`.
    public fileprivate(set) var indicator: Indicator? {
        get {
            ...
        }
        
        set {
            ...
        }
    }
  1. Swift5 支持的Result
func cacheImage(_ result: Result<ImageLoadingResult, KingfisherError>)
        {
            switch result {
            case .success(let value):
               handle(value)
            case .failure(let error):
               handle(error)
            }
        }

value是ImageLoadingResult, error是KingfisherError

  1. 禁止網絡請求的緩存
    4.1. cachePolicy
URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)

4.2 URLSessionConfiguration.ephemeral
為了防止網絡請求在我們不知道的情況下被緩存,可以使用ephmeral來進行請求

URLSession是一個可以響應發(fā)送或者接受HTTP請求的關鍵類,可以通過URLSessionConfiguration類新建URLSession實例。有以下三種方式:

URLSessionConfiguration.default
默認configuration實例創(chuàng)建方式,使用硬盤上持久化全局緩存、證書(credential)和cookie的存儲對象
URLSessionConfiguration.ephemeral
唯一跟默認configuration不一樣的是所以與會話(session)相關的數據都存儲在內存中
URLSessionConfiguration.background(withIdentifier: "ConfigurationID")
讓會話在后臺執(zhí)行上載或下載任務。即使應用程序本身被暫停或終止,傳輸仍將繼續(xù)

來說說Kingfisher簡單使用

let url = URL(string: imageURL)
imageView.kf.setImage(with: url)

kf 定義
先看 kf 的定義,返回一個包含自己的 KingfisherWrapper 對象,可以調用 Setting Image 一系列函數。

extension ImageView: KingfisherCompatible { }

extension KingfisherCompatible {
    /// Gets a namespace holder for Kingfisher compatible types.
    public var kf: KingfisherWrapper<Self> {
        get { return KingfisherWrapper(self) }
        set { }
    }
}

public struct KingfisherWrapper<Base> {
    public let base: Base
    public init(_ base: Base) {
        self.base = base
    }
}

設置方法

@discardableResult
public func setImage(
    with resource: Resource?,
    placeholder: Placeholder? = nil,
    options: KingfisherOptionsInfo? = nil,
    progressBlock: DownloadProgressBlock? = nil,
    completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
{
    return setImage(
        with: resource.map { .network($0) },
        placeholder: placeholder,
        options: options,
        progressBlock: progressBlock,
        completionHandler: completionHandler)
}

@discardableResult:表示取消不使用返回值的警告

可以看到 setImage(with: url) 內部調用了另外一個相似函數,只不過 source 參數類型從 Resource? 變成 Source? ,我們來看下這兩者的區(qū)別:

public protocol Resource {
    var cacheKey: String { get }
    var downloadURL: URL { get }
}

public enum Source {
    public enum Identifier {
        public typealias Value = UInt
        static var current: Value = 0
        static func next() -> Value {
            current += 1
            return current
        }
    }

    case network(Resource)
    case provider(ImageDataProvider)

    public var cacheKey: String {
        switch self {
        case .network(let resource): return resource.cacheKey
        case .provider(let provider): return provider.cacheKey
        }
    }
  
    public var url: URL? {
        switch self {
        case .network(let resource): return resource.downloadURL
        case .provider(_): return nil
        }
    }
}

Resource 是協議,Resource 標志這圖片來自網絡,提供cacheKey : String,downloadURL:URL。URL實現該協議,將absoluteString作為緩存的Key。

Source 是枚舉,Source 有兩種類型:.network(Resource) 和 .provider(ImageDataProvider)

imageView.kf.setImage(with: url) 可以直接傳入 url,是因為 URL 實現了 Resource 協議

extension URL: Resource {
    public var cacheKey: String { return absoluteString }
    public var downloadURL: URL { return self }
}

ImageDataProvider
ImageDataProvider是一個協議,標志這圖片來自網絡,提供cacheKey : String,func data() 來緩存與生成Image。
KingFisher提供了三種默認的Provider:

LocalFileImageDataProvider, 從本地file中讀取Image
Base64ImageDataProvider,從Base64中讀取Image
RawImageDataProvider, 從Data中讀取Image

Placeholder
Placeholder 也是一個協議,提供在ImageView上添加自身和移除自身的功能函數:

    func add(to imageView: ImageView)
    func remove(from imageView: ImageView)

Image和View有默認的配置:

extension Placeholder where Self: Image 
extension Placeholder where Self: View 

Image的默認方法是設置imageView的image為自身
View的默認方法是添加覆蓋imageView的子View

KingfisherOptionsInfo

 public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
    
    /**
Items could be added into KingfisherOptionsInfo.
*/
public enum KingfisherOptionsInfoItem {
    case targetCache(ImageCache) //設置緩存器,Kingfisher用這個緩存器來緩存展示的圖片
    case originalCache(ImageCache) //設置緩存器,Kingfisher用這個緩存器來緩存下載的原始圖片
    case downloader(ImageDownloader) //設置下載器,Kingfisher用這個下載器來下載數據
    case transition(ImageTransition) //設置下載完成之后的動畫
    case downloadPriority(Float) //0.0~1.0 設置下載優(yōu)先級
    case forceRefresh //忽視緩存
    case fromMemoryCacheOrRefresh //先嘗試從內存緩存讀取,如果沒有,則重新下載,不會讀取磁盤緩存
    case forceTransition //從緩存讀取的圖片也會進行動畫處理
    case cacheMemoryOnly //只通過內存緩存圖片
    case waitForCache //緩存完成之后才調用completion block
    case onlyFromCache  //只通過緩存讀取圖片,不會下載
    case backgroundDecode //使用圖片前線在后臺線程上解碼
    case callbackDispatchQueue(DispatchQueue?) //設置回調在那個隊列上
    case scaleFactor(CGFloat) // data轉成Image時的Scale
   .....
}

KingfisherManager
KingfisherManager主要由兩部分組成,ImageDownloader用于管理下載;ImageCache用于管理緩存。其主要函數即為:

func retrieveImage(
        with source: Source,
        options: KingfisherParsedOptionsInfo,
        completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask?

閱讀源碼之后,我們可以發(fā)現函數loadAndCacheImage負責下載及緩存圖片,函數retrieveImageFromCache負責讀出cache中的圖片。

圖片下載

ImageDownloader
下載圖片的代碼如下:

let downloader = options.downloader ?? ImageDownloader.default
guard let task = downloader.downloadImage(
    with: resource.downloadURL,
    options: options,
    completionHandler: cacheImage) else {
  return nil
}
return .download(task)

ImageDownloader

網絡請求的抽象:

private let sessionDelegate: SessionDelegate
private var session: URLSession
open var sessionConfiguration = URLSessionConfiguration.ephemeral {
  didSet {
    session.invalidateAndCancel()
    session = URLSession(configuration: sessionConfiguration, delegate: sessionDelegate, delegateQueue: nil)
  }
}

// 自定義證書的驗證邏輯
// https://www.cnblogs.com/Code-life/p/7806824.html
open weak var authenticationChallengeResponder: AuthenticationChallengeResponsable?

這里放一個Kingfisher讀取圖片緩存的邏輯圖.


image.png
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容