func tryToRetrieveImageFromCache(forKey key: String,
with url: URL,
retrieveImageTask: RetrieveImageTask,
progressBlock: DownloadProgressBlock?,
completionHandler: CompletionHandler?,
options: KingfisherOptionsInfo)
{
let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in
completionHandler?(image, error, cacheType, imageURL)
}
func handleNoCache() {
if options.onlyFromCache {
let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil)
diskTaskCompletionHandler(nil, error, .none, url)
return
}
self.downloadAndCacheImage(
with: url,
forKey: key,
retrieveImageTask: retrieveImageTask,
progressBlock: progressBlock,
completionHandler: diskTaskCompletionHandler,
options: options)
}
let targetCache = options.targetCache
// First, try to get the exactly image from cache
targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in
// If found, we could finish now.
if image != nil {
diskTaskCompletionHandler(image, nil, cacheType, url)
return
}
// If not found, and we are using a default processor, download it!
let processor = options.processor
guard processor != DefaultImageProcessor.default else {
handleNoCache()
return
}
// If processor is not the default one, we have a chance to check whether
// the original image is already in cache.
let originalCache = options.originalCache
let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor))
originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in
// If we found the original image, there is no need to download it again.
// We could just apply processor to it now.
guard let image = image else {
handleNoCache()
return
}
guard let processedImage = processor.process(item: .image(image), options: options) else {
diskTaskCompletionHandler(nil, nil, .none, url)
return
}
targetCache.store(processedImage,
original: nil,
forKey: key,
processorIdentifier:options.processor.identifier,
cacheSerializer: options.cacheSerializer,
toDisk: !options.cacheMemoryOnly,
completionHandler: {
guard options.waitForCache else { return }
let cacheType = targetCache.imageCachedType(forKey: key, processorIdentifier: options.processor.identifier)
diskTaskCompletionHandler(processedImage, nil, cacheType, url)
})
if options.waitForCache == false {
diskTaskCompletionHandler(processedImage, nil, .none, url)
}
}
}
}
这段代码比较简单,就是检索缓存,没有缓存,就尝试下载,值得注意的是这里有可能检索2次缓存, 第一次检索processor
缓存,也就是检索加工过的图片,第二次检索原图,这里的逻辑就是缓存可能存储了原图,但是没有存储加工过的图片,这样的话如果能检索到原图,只需要要加工下原图,并缓存下来,这样做的目的可以有效避免重复的网络请求,减少不必要的流量。
@discardableResult
func downloadAndCacheImage(with url: URL,
forKey key: String,
retrieveImageTask: RetrieveImageTask,
progressBlock: DownloadProgressBlock?,
completionHandler: CompletionHandler?,
options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask?
{
let downloader = options.downloader
return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options,
progressBlock: { receivedSize, totalSize in
progressBlock?(receivedSize, totalSize)
},
completionHandler: { image, error, imageURL, originalData in
let targetCache = options.targetCache
if let error = error, error.code == KingfisherError.notModified.rawValue {
// Not modified. Try to find the image from cache.
// (The image should be in cache. It should be guaranteed by the framework users.)
targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in
completionHandler?(cacheImage, nil, cacheType, url)
})
return
}
if let image = image, let originalData = originalData {
targetCache.store(image,
original: originalData,
forKey: key,
processorIdentifier:options.processor.identifier,
cacheSerializer: options.cacheSerializer,
toDisk: !options.cacheMemoryOnly,
completionHandler: {
guard options.waitForCache else { return }
let cacheType = targetCache.imageCachedType(forKey: key, processorIdentifier: options.processor.identifier)
completionHandler?(image, nil, cacheType, url)
})
// 原图 ??
// TODO: 003 completionHandler 为什么等于nil
if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
let originalCache = options.originalCache
let defaultProcessor = DefaultImageProcessor.default
if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) {
originalCache.store(originalImage,
original: originalData,
forKey: key,
processorIdentifier: defaultProcessor.identifier,
cacheSerializer: options.cacheSerializer,
toDisk: !options.cacheMemoryOnly,
completionHandler: nil)
}
}
}
if options.waitForCache == false {
completionHandler?(image, error, .none, url)
}
})
}
这段逻辑比较confuse,要结合上下文来看,主要是store逻辑有点confuse,store的时候,把image和originalData一起传进去,而事实上最终存储的是image这个实参,这里的image在回调之前,已经经过process,和modify了, 所以如果是processor的话,直接存储这个image, 如果用户与此同时要求存储原图的话,那么使用DefaultImageProcessor 序列化 originalData,得到原图image,然后再存储该image.
网友评论