一、前言
Kingfisher Github地址
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理解为协议约束,需要遵守KingfisherCompatible
协议的类型,例如这里的就是Image
。
4.ImageView遵守KingfisherCompatible协议。
然后就可以使用了,ImageView+Kingfisher.swift
中:
extension Kingfisher where Base: ImageView {
// 省略
}
复制代码
这里看上去是在给Kingfisher
添加扩展,其实是给ImageView
,因为Kingfisher
中的base
属性其实就是ImageView
的实例对象,我们只需要在添加的方法中用base
代替我们直接给UIImageView添加扩展中的self
就行了。
但是目前这种写法有一个限制,那就是结构体无法使用,因为Kingfisher
中的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
工作的主要流程,细节待后续文章分享。其中还有很多枝叶操作也是很有意思的。