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
工做的主要流程,细节待后续文章分享。其中还有不少枝叶操做也是颇有意思的。