Kingfisher源码解析之ImagePrefetcher

Kingfisher源码解析系列,因为水平有限,哪里有错,肯请不吝赐教api

ImagePrefetcher提供了哪些功能

ImagePrefetcher是Kingfisher提供预加载功能的一个类,提供了一下功能数组

  • start():开启预加载
  • stop():中止预加载
  • maxConcurrentDownloads:设置最大缓存并发量
  • progressBlock和progressSourceBlock:缓存进度的回调
  • completionHandler和completionSourceHandler:缓存结束的回调

ImagePrefetcher预加载的流程图

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函数时,会出现如下几种状况以及对应的结果bash

  1. 调用stop时,已经预加载结束了,因为已经结束,会直接返回
  2. 调用stop时,如今已经有正在下载图片的任务了,会取消所全部请求,而后请求就会走结束的回调,在结束的回调里把剩下的未加载的数据放入到失败的数据源的数组中,调用结束回调
  3. 调用stop时,尚未正在下载的任务,会继续预加载数据,直到结束,或者有一个请求结束

对于状况1和状况2都是合理的,而且是绝大部分都会是状况1和状况2,对于状况3,调用stop时并无真正的去中止,可是这种状况也是较少出现的。网络

对于stop方法,喵神的注释是这样的并发

/// Stops current downloading progress, and cancel any future prefetching activity that might be occuring.异步

缓存进度和缓存结束的回调为何要各有2个

我第一次看代码,就想为何要有2个呢?为何这么设计呢?这里以缓存进度的回调举例,它们两个的缘由是同样的。先来看下定义,async

public typealias PrefetcherProgressBlock =
    ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)

public typealias PrefetcherSourceProgressBlock =
    ((_ skippedSources: [Source], _ failedSources: [Source], _ completedSources: [Source]) -> Void)
复制代码

咱们发现基本是同样的,只是回调里的参数类型不同,一个Resource,另外一个Source。若是你对这2个类型比较了解,想必你应该能猜到这么设计的缘由了。ide

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

相关文章
相关标签/搜索