首先一个 URL 对应一个FetchLoad
class ImageFetchLoad {
var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]()
var responseData = NSMutableData()
var downloadTaskCount = 0
var downloadTask: RetrieveImageDownloadTask?
var cancelSemaphore: DispatchSemaphore?
}
[URL: FetchLoad];
当每次download 的时候 ,先去匹配这个FetchLoad,如果FetchLoad存在且downloadTaskCount != 0的话,就不会发起新的网络请求,
FetchLoad 使用downloadTaskCount 作为task引用计数,也就是说同一个URL的请求,不会造成重复的网络请求,只会造成downloadTaskCount+=1,当请求结束后,
会挨个调用contents中的闭包回调,cancelSemaphore 作为线程同步的信号,用来保证线程安全的,整个流程就是这样。
而在回调的时候,会触发processImage方法,该方法会根据content.options
加工和修改图片。
private func processImage(for task: URLSessionTask, url: URL) {
guard let downloader = downloadHolder else {
return
}
// We are on main queue when receiving this.
downloader.processQueue.async {
guard let fetchLoad = downloader.fetchLoad(for: url) else {
return
}
self.cleanFetchLoad(for: url)
let data: Data?
let fetchedData = fetchLoad.responseData as Data
if let delegate = downloader.delegate {
data = delegate.imageDownloader(downloader, didDownload: fetchedData, for: url)
} else {
data = fetchedData
}
// Cache the processed images. So we do not need to re-process the image if using the same processor.
// Key is the identifier of processor.
var imageCache: [String: Image] = [:]
for content in fetchLoad.contents {
let options = content.options
let completionHandler = content.callback.completionHandler
let callbackQueue = options.callbackDispatchQueue
let processor = options.processor
var image = imageCache[processor.identifier]
if let data = data, image == nil {
image = processor.process(item: .data(data), options: options)
// Add the processed image to cache.
// If `image` is nil, nothing will happen (since the key is not existing before).
imageCache[processor.identifier] = image
}
if let image = image {
downloader.delegate?.imageDownloader(downloader, didDownload: image, for: url, with: task.response)
let imageModifier = options.imageModifier
let finalImage = imageModifier.modify(image)
if options.backgroundDecode {
let decodedImage = finalImage.kf.decoded
callbackQueue.safeAsync { completionHandler?(decodedImage, nil, url, data) }
} else {
callbackQueue.safeAsync { completionHandler?(finalImage, nil, url, data) }
}
} else {
if let res = task.response as? HTTPURLResponse , res.statusCode == 304 {
let notModified = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notModified.rawValue, userInfo: nil)
completionHandler?(nil, notModified, url, nil)
continue
}
let badData = NSError(domain: KingfisherErrorDomain, code: KingfisherError.badData.rawValue, userInfo: nil)
callbackQueue.safeAsync { completionHandler?(nil, badData, url, nil) }
}
}
}
}