Kingfisher
部分收获:
- 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 {
return Kingfisher(self)
}
}
extension ImageView: KingfisherCompatible { }
extension Image: KingfisherCompatible { }
给ImageView和image遵守了一个协议 KingfisherCompatible
从而实现了
imageView.kf 就等同于 let kf: Kingfisher = Kingfisher
- 读写权限不同
fileprivate(set)
/// It will be `nil` if `indicatorType` is `.none`.
public fileprivate(set) var indicator: Indicator? {
get {
...
}
set {
...
}
}
- Swift5 支持的Result
func cacheImage(_ result: Result)
{
switch result {
case .success(let value):
handle(value)
case .failure(let error):
handle(error)
}
}
value是ImageLoadingResult, error是KingfisherError
- 禁止网络请求的缓存
4.1. cachePolicy
URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
4.2 URLSessionConfiguration.ephemeral
为了防止网络请求在我们不知道的情况下被缓存,可以使用ephmeral来进行请求
URLSession是一个可以响应发送或者接受HTTP请求的关键类,可以通过URLSessionConfiguration类新建URLSession实例。有以下三种方式:
URLSessionConfiguration.default
默认configuration实例创建方式,使用硬盘上持久化全局缓存、证书(credential)和cookie的存储对象
URLSessionConfiguration.ephemeral
唯一跟默认configuration不一样的是所以与会话(session)相关的数据都存储在内存中
URLSessionConfiguration.background(withIdentifier: "ConfigurationID")
让会话在后台执行上载或下载任务。即使应用程序本身被暂停或终止,传输仍将继续
来说说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 {
get { return KingfisherWrapper(self) }
set { }
}
}
public struct KingfisherWrapper {
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) -> 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? ,我们来看下这两者的区别:
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 设置下载优先级
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) -> Void)?) -> DownloadTask?
阅读源码之后,我们可以发现函数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读取图片缓存的逻辑图.