Kingfisher
是swift
語言編寫的一款很是受歡迎的圖片加載庫,功能和OC
語言編寫的SDWebImage
相似。做者貓神是我初入iOS開發到如今都很崇拜的偶像。github
imageView.kf.setImage(with: imageURL)
複製代碼
從上面的使用方法能夠看出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
協議的類型,例如這裏的就是Image
。spa
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)
}
}
複製代碼
這裏和上面相似就不贅述了,咱們本身的寫的工具庫或者三方也能自定義這樣的寫法來避免衝突.
關鍵點:
KingfisherOptionsInfo
是一個枚舉,用來配置庫中的信息,例如後臺線程解碼圖片,和出現的動畫等等.
這個方法主要配置了一些信息,例如默認圖片和加載指示器等等.而後就將獲取圖片的任務轉交給了KingfisherManager
。 獲取圖片成功後,根據配置的顯示動畫,獲取顯示並動畫顯示圖片
公開分類方法
public func setImage(with resource: Resource?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil)
-> RetrieveImageTask
{
// 省略
// -> 3 KingfisherOptionsInfo配置後等,將任務交給KingfisherManager。
// 根據配置的動畫,獲取顯示並動畫顯示圖片
}
複製代碼
關鍵點: KingfisherManager,用來獲取圖片和下載緩存圖片。
獲取圖片:
public func retrieveImage(with resource: Resource, options: KingfisherOptionsInfo?, progressBlock: DownloadProgressBlock?, completionHandler: CompletionHandler?) -> RetrieveImageTask
{
// 省略
// -> 4.1 若是強制刷新,去下載並緩存
// -> 4.2 從緩存中獲取
}
複製代碼
關鍵點: 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
}
複製代碼
直接將任務交給了ImageCache,嘗試從緩存中獲取圖片
func tryToRetrieveImageFromCache(forKey key: String, with url: URL, retrieveImageTask: RetrieveImageTask, progressBlock: DownloadProgressBlock?, completionHandler: CompletionHandler?, options: KingfisherOptionsInfo)
{
// 省略
// 5.2 -> 直接將任務交給了ImageCache
}
複製代碼
關鍵點: ImageDownloader,圖片下載器
下載圖片
downloadImage(with url: URL,
retrieveImageTask: RetrieveImageTask? = nil,
options: KingfisherOptionsInfo? = nil,
progressBlock: ImageDownloaderProgressBlock? = nil,
completionHandler: ImageDownloaderCompletionHandler? = nil)
-> RetrieveImageDownloadTask?
{
// 省略
// -> 完
}
複製代碼
緩存圖片,分爲內存緩存和磁盤緩存
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)
{
// 省略
// 內存緩存
// 磁盤緩存
// -> 完
}
複製代碼
根據配置從內存獲取或者從磁盤獲取獲取圖片.
@discardableResult
open func retrieveImage(forKey key: String, options: KingfisherOptionsInfo?, completionHandler: ((Image?, CacheType) -> Void)?)
-> RetrieveImageDiskTask?
{
// 省略
// 根據配置從內存獲取或者從磁盤獲取
}
複製代碼
這篇這一篇文章主要分析Kingfisher
工做的主要流程,細節待後續文章分享。其中還有不少枝葉操做也是頗有意思的。