KingFisher SessionManager core logic


    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.

你可能感兴趣的:(KingFisher SessionManager core logic)