一句最简单的调用Kingfisher
的代码:
imgView.kf.setImage(with: URL.init(string: ""))
对象通过调用kf
返回一个经过KingfisherWrapper
包装的自己,而进入kf
相关定义我们可以很明确地了解到kf
是一个计算性属性,唯一的作用就是返回一个经过KingfisherWrapper
包装的调用者,这个属性被声明并实现于KingfisherCompatible
协议的扩展,我们知道,Swift
支持在协议的扩展中声明并默认实现属性、初始化函数以及函数等等,而在KingfisherCompatible
提供了kf
属性的默认实现后,通过:
extension Image: KingfisherCompatible { }
#if !os(watchOS)
extension ImageView: KingfisherCompatible { }
extension Button: KingfisherCompatible { }
让我们所知的三类对象遵循该协议,它们也就默认实现了kf
属性。
而通过观察KingfisherWrapper
的主代码我们也可以知道,KingfisherWrapper
主代码仅仅是将调用者保存在自身中,关于Image、UIButton、UIImageView
的实现都被分别写在相应的扩展中。
而当我们进入setImage
函数的源码可知,该函数位于KingfisherWrapper
的扩展,并且是实现在当KingfisherWrapper
持有的Base
属于UIImageView
类型的扩展中。
setImage
函数接收一个遵循Resource
协议的形参,而Kingfisher
在源码中实现了URL
的扩展遵循Resource
协议
extension URL: Resource {
public var cacheKey: String { return absoluteString }
public var downloadURL: URL { return self }
}
其作用在于使用计算性属性返回String类型的URL地址作为缓存标识符。
而在setImage
函数内部,遵循Resource
协议的URL地址会通过map高阶函数转化为Source
枚举对象,这个枚举对象的作用是划分对象是网络图片亦或是本地图片,同时,实现一些附属属性以及计算性属性。而我们的URL地址在此被包裹为Source.network
类型的枚举对象进入下一阶段。
guard let source = source else {
mutatingSelf.placeholder = placeholder
mutatingSelf.taskIdentifier = nil
completionHandler?(.failure(KingfisherError.imageSettingError(reason: .emptySource)))
return nil
}
大部分的网络请求数据以外的业务逻辑都暂时先跳过,但这里的代码的表现形式的内部源码与Kingfisher
的一些比较重要的业务逻辑有比较深的关系,因此提前写出。
我们知道,这里的调用者mutatingSelf
并不是我们外部的调用者,而是包裹了我们的调用者的KingfisherWrapper
对象,而代码中的placeholder、taskIdentifier
以及这里没有写出来的indicatorType、indicator、imageTask
都存在于KingfisherWrapper
当包裹对象为ImageView
时的扩展中,它们都实现了相同的概念,利用一个Void
对象的指针地址作为唯一标识符调用objc_set
和objc_get
在KingfisherWrapper
对象中进行取值和赋值。
而我们知道,Void
对象指针作为唯一标识符的Key
将相应的数据进行存储或者取出说明了objc_set
和objc_get
本身能在一个对象的属性列表中插入没有声明过的属性,并对这个属性进行赋值取值。
let task = KingfisherManager.shared.retrieveImage(
with: source,
options: options,
completionHandler: { result in
}
这一段是Kingfisher
的核心代码,让我们看看它的内部实现是什么样子的。
因为KingfisherManager
承担了整个框架的核心逻辑,并且极大部分的代码都与这个类有关,如果我们直接从KingfisherManager
的单例方法初始化函数开始进行解析,只会看到大量分别承担不同功能的对象,并且这些承担不同功能的对象内部还有分门别类的子对象,直到我们看到作为基本单元的最底层的子对象为止,这样就相当于盲人摸象,管中窥豹难见一斑,所以,先从retrieveImage
函数的功能实现来反向推测KingfisherManager
实现了什么样的对象来帮助自己实现什么样的功能。
//options: KingfisherParsedOptionsInfo
if options.forceRefresh {
return loadAndCacheImage(
source: source,
options: options,
completionHandler: completionHandler)?.value
}
我们知道,当我们调用kf.setImage
函数时,KingfisherOptionsInfo
作为可选形参,其实质是一个KingfisherOptionsInfoItem
数组,而在setImage
函数内部,我们将KingfisherOptionsInfo
转化为KingfisherParsedOptionsInfo
对象,其内部通过for循环将KingfisherOptionsInfoItem
一个个地配置成相应的KingfisherParsedOptionsInfo
成员变量,就如上述代码的forceRefresh
布尔值,我们可以通过:
var options = KingfisherOptionsInfo.init()
options.append(KingfisherOptionsInfoItem.forceRefresh)
来添加到options
形参中。
我们来到loadAndCacheImage
函数的内部实现中,最先看到的是一个巨大的内部实现函数cacheImage
,我们暂时先跳过这个函数,进入到下一部分:
switch source {
case .network(let resource):
let downloader = options.downloader ?? self.downloader
guard let task = downloader.downloadImage(
with: resource.downloadURL,
options: options,
completionHandler: cacheImage) else {
return nil
}
return .download(task)
case .provider(let provider):
provideImage(provider: provider, options: options, completionHandler: cacheImage)
return .dataProviding
}
如果我们是网络图片的话就会进入到.network
逻辑中,KingfisherManager
在单例中调用初始化函数,默认生成了一个名为default
的下载器,如果我们没有在KingfisherOptionsInfo
中另外提供下载器,那么它就会使用默认下载器执行代码。
当下载机调用downloadImage
函数时,我们将cacheImage
函数作为形参传入,而下载器内部进行下载工作时无论成功与否,最终都会回调给cacheImage
函数,而cacheImage
函数正如其名,负责执行成功后的缓存工作以及图像回调还有失败后的失败回调。
缓存的相关逻辑之后再说,先进入下载器downloadImage
函数的内部构造(因为函数内部代码比较庞大,因此分段解析):
// Creates default request.
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
request.httpShouldUsePipelining = requestsUsePipelining
生成一个网络请求,downloadTimeout
和requestsUsePipelining
是下载器内部属性,想要修改就只能在options
中初始化一个下载器替代默认下载器重新配置相关属性。
if let requestModifier = options.requestModifier {
// Modifies request before sending.
guard let r = requestModifier.modified(for: request) else {
options.callbackQueue.execute {
completionHandler?(.failure(KingfisherError.requestError(reason: .emptyRequest)))
}
return nil
}
request = r
}
如果options
含有遵循ImageDownloadRequestModifier
协议的对象则调用协议函数修改网络请求,进行下一步,options
的callbackQueue
默认实现在主线程进行安全的异步回调,感兴趣的可以看一下CallbackQueue
的内部实现。
// There is a possibility that request modifier changed the url to `nil` or empty.
// In this case, throw an error.
guard let url = request.url, !url.absoluteString.isEmpty else {
options.callbackQueue.execute {
completionHandler?(.failure(KingfisherError.requestError(reason: .invalidURL(request: request))))
}
return nil
}
判断URL
是否为空的判断,如注释所说,外部形参传入的Source
为空的判断很早就做过了,这里主要是实现ImageDownloadRequestModifier
协议函数的对象的相关判断。
// Wraps `completionHandler` to `onCompleted` respectively.
let onCompleted = completionHandler.map {
block -> Delegate, Void> in
let delegate = Delegate, Void>()
delegate.delegate(on: self) { (_, callback) in
block(callback)
}
return delegate
}
// SessionDataTask.TaskCallback is a wrapper for `onCompleted` and `options` (for processor info)
let callback = SessionDataTask.TaskCallback(
onCompleted: onCompleted,
options: options
)
// Ready to start download. Add it to session task manager (`sessionHandler`)
let downloadTask: DownloadTask
if let existingTask = sessionDelegate.task(for: url) {
downloadTask = sessionDelegate.append(existingTask, url: url, callback: callback)
} else {
let sessionDataTask = session.dataTask(with: request)
sessionDataTask.priority = options.downloadPriority
downloadTask = sessionDelegate.add(sessionDataTask, url: url, callback: callback)
}
let sessionTask = downloadTask.sessionTask
这部分代码紧密嵌合在一起,完成网络请求之前的最后一部分工作,我们一点点捋过来。
首先是第一部分,初始化一个onCompleted
对象,其实质是将外界传递进来的completionHandler
用一个Delegate
类进行容纳,这个类的内部实现不多,其主要功能就是容纳相应的两个泛型,内部命名为输入与输出,用一个带闭包的函数将传入的target置为弱引用并按照原本的格式返回两个泛型对象,达到避免循环引用的作用,可以说,这个类是防止循环引用而对闭包的封装。
然后,实现Delegate
实例对象的闭包函数,并将delegate
实例对象通过map高阶函数返回命名为onCompleted
,当onCompleted
在其他地方被调用时,这里实现的闭包就会被调用,并将最终的值传递给外界,即传递到cacheImage
函数中。
第二部分,传入onComplete
对象和配置对象,生成SessionDataTask.TaskCallback
结构体的实例对象。
第三部分,判断我们的sessionDelegate
实例对象内部维护的以URL
作为Key,SessionDataTask
作为Value的哈希表中是否已有相应的实例对象,若存在则使用SessionDataTask
实例对象生成DownloadTask
对象,若不存在,则从URLSession
中获取URLSessionTask
对象,并将其通过sessionDelegate
包装为SessionDataTask
对象,实现其相应闭包,插入到哈希表中并生成DownloadTask
对象。
整个网络请求前的最后一个环节便完成了,SessionDelegate
及SessionDataTask
内部有相当庞大的代码,难以在此一一描述,只能暂时描述其功能表现为“SessionDelegate负责实现URLSession的协议方法,负责在各个代理回调中处理相应的业务逻辑,同时暴露监听回调给外部,通常这个实现其监听回调的对象是ImageDownloader”。
// Start the session task if not started yet.
if !sessionTask.started {
sessionTask.onTaskDone.delegate(on: self) { (self, done) in
// Underlying downloading finishes.
// result: Result<(Data, URLResponse?)>, callbacks: [TaskCallback]
let (result, callbacks) = done
// Before processing the downloaded data.
do {
let value = try result.get()
self.delegate?.imageDownloader(
self,
didFinishDownloadingImageForURL: url,
with: value.1,
error: nil
)
} catch {
self.delegate?.imageDownloader(
self,
didFinishDownloadingImageForURL: url,
with: nil,
error: error
)
}
switch result {
// Download finished. Now process the data to an image.
case .success(let (data, response)):
let processor = ImageDataProcessor(
data: data, callbacks: callbacks, processingQueue: options.processingQueue)
processor.onImageProcessed.delegate(on: self) { (self, result) in
// `onImageProcessed` will be called for `callbacks.count` times, with each
// `SessionDataTask.TaskCallback` as the input parameter.
// result: Result, callback: SessionDataTask.TaskCallback
let (result, callback) = result
if let image = try? result.get() {
self.delegate?.imageDownloader(self, didDownload: image, for: url, with: response)
}
let imageResult = result.map { ImageLoadingResult(image: $0, url: url, originalData: data) }
let queue = callback.options.callbackQueue
queue.execute { callback.onCompleted?.call(imageResult) }
}
processor.process()
case .failure(let error):
callbacks.forEach { callback in
let queue = callback.options.callbackQueue
queue.execute { callback.onCompleted?.call(.failure(error)) }
}
}
}
delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request)
sessionTask.resume()
}
首先先看代码的最后数行,分别是通知代理者的协议函数,与SessionDataTask
调用resume
函数,其内部调用了URLSessionDataTask
的resume
函数,由此进行网络请求,而当网络请求完成时调用sessionTask.onTaskDone.delegate(on: self)
的闭包实现。
通过分析SessionDelegate
关于遵循URLSessionDataDelegate
协议的实现协议回调我们可以知道,didReceive data
协议函数中负责向SessionDataTask
实例对象中不断输写图像数据,而didCompleteWithError
协议函数则是当数据全部传输完毕时将完整的图像数据交给内部的onCompleted
函数,而onCompleted
函数内调用SessionDataTask
实例对象的onTaskDone
的闭包实现,由此,将图像数据传递给位于Imagedownloader
中的SessionDataTask
的onTaskDone
的闭包实现中。
在onTaskDone
的闭包实现中我们可以先着眼于这行代码:
let (result, callbacks) = done
代码很简单,就是对元组进行分割,我们需要着眼的是,这个callbacks从何而来。
在SessionDelegate
的onCompleted
函数我们可以知道,这个callbacks的数据实际上是执行这次请求任务的SessionDataTask
的实例对象中的计算性属性callbacks
从哈希表成员变量callbacksStore
中获取的,那callbacksStore
的数据从何而来呢?
观察SessionDataTask
的内部代码我们可以发现,唯一向callbacksStore
表中插入数据的函数就是addCallback
函数,而这个函数就是在我们之前看过的ImageDownloader
的初始化DownloadTask
的相关代码中。
let downloadTask: DownloadTask
if let existingTask = sessionDelegate.task(for: url) {
downloadTask = sessionDelegate.append(existingTask, url: url, callback: callback)
} else {
let sessionDataTask = session.dataTask(with: request)
sessionDataTask.priority = options.downloadPriority
downloadTask = sessionDelegate.add(sessionDataTask, url: url, callback: callback)
}
sessionDelegate
调用append
或者add
函数时调用了SessionDataTask
的addCallback
函数,传递了SessionDataTask.TaskCallback
实例对象,而这个实例对象在初始化时容纳了容纳completionHandler
的onCompleted
对象,而我们知道,completionHandler
被调用时,我们位于KingfisherManager
中的cacheImage
函数就会被调用,在cacheImage
中完成缓存图片和继续向外传递图片数据的工作。
所以我们就知道了,当位于ImageDownloader
中的sessionTask.onTaskDone
的闭包实现中的callbacks
数组中的callback
对象调用onCompleted
时,位于KingfisherManager
的cacheImage
就会被调用。
回到onTaskDone
的闭包实现,闭包中当进入下载成功的枚举时,初始化了一个ImageDataProcessor
对象,容纳我们上面提到的callbacks
。这个ImageDataProcessor
对象的功能是负责将Data
形式的图像数据转化为Image
对象,通过实现onImageProcessed
闭包,当ImageDataProcessor
调用process
函数,运用异步子线程调用doProcess
函数内部处理图片完成后获得回调,而onImageProcessed
回调时将Image
数据插入到ImageLoadingResult
中,回到主线程,进行最终的onCompleted
回调,调用cacheImage
函数,进行相应的工作。
由此,整个网络请求下载图片的逻辑就结束了,接下来是ImageCache
相关的内存缓存和磁盘缓存的业务逻辑。