一、前言
Kingfisher
是swift
語言編寫的一款非常受歡迎的圖片載入庫,功能和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
工作的主要流程,細節待後續文章分享。其中還有很多枝葉操作也是很有意思的。