Kingfisher源码解析系列,由于水平有限,哪里有错,肯请不吝赐教
- Kingfisher源码解析之使用
- Kingfisher源码解析之Options解释
- Kingfisher源码解析之加载流程
- Kingfisher源码解析之加载动图
- Kingfisher源码解析之ImageCache
- Kingfisher源码解析之Processor和CacheSerializer
- Kingfisher源码解析之ImagePrefetcher
ImagePrefetcher提供了哪些功能
ImagePrefetcher是Kingfisher提供预加载功能的一个类,提供了一下功能
- start():开启预加载
- stop():停止预加载
- maxConcurrentDownloads:设置最大缓存并发量
- progressBlock和progressSourceBlock:缓存进度的回调
- completionHandler和completionSourceHandler:缓存结束的回调
ImagePrefetcher预加载的流程图
ImagePrefetcher两个问题
当调用stop()函数之后的逻辑
先来看下stop函数的实现,实现比较简单,在预加载的队列里异步的执行把标志位stopped设置为true,并且取消当前所有未完成的下载任务,看起来很简单。
public func stop() {
pretchQueue.async {
if self.finished { return }
self.stopped = true
self.tasks.values.forEach { $0.cancel() }
}
}
但是stopped这个标志位只在网络请求结束的回调里去判断了,这就会发生一些歧义,交给读者去判断Kingfisher这么做是否是合理的?当调用stop函数时,会出现以下几种情况以及对应的结果
- 调用stop时,已经预加载结束了,由于已经结束,会直接返回
- 调用stop时,现在已经有正在下载图片的任务了,会取消所所有请求,然后请求就会走结束的回调,在结束的回调里把剩下的未加载的数据放入到失败的数据源的数组中,调用结束回调
- 调用stop时,还没有正在下载的任务,会继续预加载数据,直到结束,或者有一个请求结束
对于情况1和情况2都是合理的,并且是绝大部分都会是情况1和情况2,对于情况3,调用stop时并没有真正的去停止,但是这种情况也是较少出现的。
对于stop方法,喵神的注释是这样的
/// Stops current downloading progress, and cancel any future prefetching activity that might be occuring.
缓存进度和缓存结束的回调为什么要各有2个
我第一次看代码,就想为什么要有2个呢?为什么这么设计呢?这里以缓存进度的回调举例,它们两个的原因是一样的。先来看下定义,
public typealias PrefetcherProgressBlock =
((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
public typealias PrefetcherSourceProgressBlock =
((_ skippedSources: [Source], _ failedSources: [Source], _ completedSources: [Source]) -> Void)
我们发现基本是一样的,只是回调里的参数类型不一样,一个Resource,另一个Source。如果你对这2个类型比较了解,想必你应该能猜到这么设计的原因了。
Source是一个枚举,Kingfisher中为UIImage提供数据源用的,定义如下,有2个case,一个是关联了Resource,另一个关联了ImageDataProvider
public enum Source {
case network(Resource)
case provider(ImageDataProvider)
}
Resource是一个协议,定义如下,提供数据源的真正类型之一,一般用于加载网络图片
public protocol Resource {
var cacheKey: String { get }
var downloadURL: URL { get }
}
ImageDataProvider也是一个协议,定义如下,提供数据源的另一个真正类型,一般用于本地图片
public protocol ImageDataProvider {
var cacheKey: String { get }
func data(handler: @escaping (Result<Data, Error>) -> Void)
}
回答上面的问题,由于我们一般情况下预加载的都是网络图片,因此提供一个方便我们使用的回调,但为了覆盖到所有情况,就提供了2个情况的回调,这个在ImagePrefetcher的便利初始化方法里我们就能看出来,当使用[URL](注:在URL的扩展里实现了Resource协议)或者[Resource]初始化的时候,就使用PrefetcherProgressBlock,当使用[Source]初始化时,就使用的PrefetcherSourceProgressBlock