Kingfisher 出现 notCurrentSourceTask 错误时图片是否已下载缓存?
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
在 Kingfisher 中,你对一个 image view 调用setImage加载url1,在它完成前又对同一个 view 设置了url2,url1的 completion handler 就会收到.failure(.imageSettingError(.notCurrentSourceTask))(错误码 5002)。常见疑问是:这个错误会不会连带中断下载?答案是不会——Kingfisher 官方文档明确说明,此时url1的下载任务照常完成,下载到的图片数据会被处理并正常写入缓存;这个错误只表示结果回来时,view 已经不再等待这个资源,所以图片没有被显示出来。
下面的内容说明这个错误的产生机制、如何判断底层任务到底是成功还是失败、以及如何核实图片确实已进入缓存。
notCurrentSourceTask 是如何产生的
错误定义在 KingfisherError.swift:
- 每次调用
setImage都会签发一个新的任务标识并覆盖旧任务的标识(见 ImageView+Kingfisher.swift)。 - 旧任务最终完成时,Kingfisher 发现当前 view 的任务标识已经不是这个任务的了,于是把结果包装成
.notCurrentSourceTask报给旧任务的 completion handler,无论该任务本身是成功还是失败。 - 该错误的关联值有三个:
result(底层任务成功时的RetrieveImageResult,失败时为nil)、error(底层任务失败时的错误,成功时为nil)、source(该任务原始的Source)。 - 错误域为
com.onevcat.Kingfisher.Error,notCurrentSourceTask对应的错误码是 5002。 - 需要判断错误类型时可以用
KingfisherError.isNotCurrentTask属性(见 KingfisherError.swift),它返回true表示"新任务开始后旧任务完成,但 view 没有被更新"。
官方性能文档 Topic_PerformanceTips.md 给出的文档示例:
imageView.kf.setImage(with: url1) { result in // `result` is `.failure(.imageSettingError(.notCurrentSourceTask))` // due to another `setImage` below. // // But the download (and cache) is done normally. } // Set again immediately. imageView.kf.setImage(with: url2) { result in // `result` is `.success` }并明确写道:
Even if the setting for
url1ends in a.failurebecause it was overridden byurl2, the download task itself completes. The downloaded image data is processed and cached accordingly.
所以收到 5002 时,要区分两种情况,依据是关联值:
| 关联值 | 含义 |
|---|---|
result非nil | url1本身下载成功,图片已处理并缓存,只是没有显示到 view 上 |
error非nil | url1底层任务确实失败了(例如网络错误),且随后被新任务覆盖 |
仓库测试 ImageViewExtensionTests.swift 中的testDownloadForMultipleURLs验证了第一种情况:第一个setImage的回调收到notCurrentSourceTask,但错误关联的result里带有下载到的图片;而 view 上实际显示的是第二个 URL 的图像。
如何核实图片确实已进入缓存
下载"完成"不等于你马上能用isCached检查到,因为磁盘缓存是异步写入的。按 CommonTasks_Cache.md 的说明:
确定缓存 key。默认缓存 key 是把 URL 转成字符串生成的,网络 URL 使用
absoluteString(见 Resource.swift)。所以直接用url1.absoluteString作为 key 即可;如果你用ImageResource(downloadURL:cacheKey:)自定义过 key,则用那个 key。检查缓存位置:
let cache = ImageCache.default let cached = cache.isCached(forKey: url1.absoluteString) // 想知道缓存在哪里:`.memory`、`.disk` 或 `.none` let cacheType = cache.imageCachedType(forKey: url1.absoluteString)如果加载时用了 processor,处理后的图片是带 processor 标识缓存的,检查时必须带上:
let processor = RoundCornerImageProcessor(cornerRadius: 20) cache.isCached(forKey: url1.absoluteString, processorIdentifier: processor.identifier)注意磁盘缓存的异步性。文档指出:图片写入磁盘缓存是异步的,view 扩展方法的 completion handler 执行时磁盘缓存可能尚未写完——此时查磁盘缓存可能得到
nil;但内存缓存操作是同步的,图片一定已经可以命中内存缓存。如果你的逻辑依赖"handler 返回时磁盘缓存必须存在",在setImage时加.waitForCache选项,Kingfisher 会等磁盘缓存操作完成后再执行 handler:imageView.kf.setImage(with: url1, options: [.waitForCache]) { _ in ImageCache.default.retrieveImageInDiskCache(forKey: url1.absoluteString) { result in // 此时磁盘缓存写入已完成 } }如果
url1加载时没有.waitForCache,而在旧任务的 handler 里立刻查磁盘得到nil,这属于文档说明的异步时序,不代表下载没完成——可以稍后再查,或检查内存缓存。
如果确认这张图片不会再显示,可以取消下载
上面的行为意味着url1即使没显示也消耗了网络、CPU、内存和电量。文档建议:如果url1的图片之后还可能再次显示给用户,这些开销换来的是缓存命中,值得;如果你确定它不再需要,就在设置新图片前取消旧任务,让旧 handler 收到.failure(.requestError(.taskCancelled))而不是 5002:
imageView.kf.setImage(with: url1) { result in // `result` is `.failure(.requestError(.taskCancelled))` // Now the download task is cancelled. } imageView.kf.cancelDownloadTask() imageView.kf.setImage(with: url2) { result in // `result` is `.success` }cancelDownloadTask()只对还在运行中的下载任务生效,已完成的下载不受影响(见 ImageView+Kingfisher.swift)。在 table view / collection view 中快速滚动时,文档给出的做法是在didEndDisplaying里取消即将消失 cell 的未完成任务:
func collectionView( _ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // This will cancel the unfinished downloading task when the cell disappearing. cell.imageView.kf.cancelDownloadTask() }这样处理后,被取消任务的回调会收到.requestError(.taskCancelled)(可用KingfisherError.isTaskCancelled判断),而不是notCurrentSourceTask——两种错误的区别正好可以用来确认"下载到底跑了没有"。
小结与边界
- 收到
.notCurrentSourceTask(5002)时,图片是否已下载缓存取决于关联值:result非nil表示底层下载成功、数据已处理并按默认缓存策略写入内存与磁盘缓存;error非nil表示底层任务本身失败。 - 核实时用
ImageCache.default.isCached(forKey:)/imageCachedType(forKey:),带上 processor identifier(如有),并记住磁盘缓存写入是异步的,必要时用.waitForCache。 - 5002 只是"设置"阶段的错误,不代表加载流程失败;live photo 场景有对应的
notCurrentLivePhotoSourceTask(错误码 5005),机制相同。 - 详细文档可参考 Topic_PerformanceTips.md 与 CommonTasks_Cache.md。
【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考