SDWebImageDownloader 源码阅读记录(三)
SDWebImage的图片下载是由SDWebImageDownloader这个类来实现的,它是一个异步下载管理器,下载过程中增加了对图片加载做了优化的处理。而真正实现图片下载的是自定义的一个Operation操作,将该操作加入到下载管理器的操作队列downloadQueue中,Operation操作依赖系统提供的NSURLSession类实现图片的下载。
Asynchronous downloader dedicated and optimized for image loading.
专用的并且优化的图片异步下载器.
我们还是从SDWebImageDownloader的核心方法入手:
|
|
该方法的大部分代码都放到了一个createCallback的回调中:
|
|
它为这个下载的操作添加回调的块, 在下载进行时, 或者在下载结束时执行一些操作, 先来阅读一下这个方法的源代码:
|
|
注释有说,这个 URL 参数不能为空,因为它要作为存储 callbacks 字典的 key,如果它为 nil 则会马上调用 completed block 返回 nil 图片和 nil 数据。self.URLCallbacks 是一个 NSMutableDictionary ,它以 URL 作为 key ,维护一个可变数组 callbacksForURL,这个数组里又会存放一个一个的 NSMutableDictionary 用来存储两个 callback 回调方法,分别是以 kProgressCallbackKey 为 key 的 progressBlock 和 以 kCompletedCallbackKey 为 key 的 completedBlock 。代码里还有一个 BOOL first 的变量,如果发现 self.URLCallbacks 中没有这个 URL 的回调数组,那这个 URL 此时就是第一次请求(此时没有相同的 URL 在请求),会调用 createCallback(); 来创建下载的操作,而发现 self.URLCallbacks 中有这个 URL 的回调数组的话,则将对应的那两个回调方法存进 NSMutableDictionary ,在放到之前的回调数组中,且不会再调用 createCallback(); ,这样相同的 URL 不会重复请求下载。当第一个请求下载成功之后,会遍历这个回调数组,将数组里所有的 callback 都执行一遍。这么做的目的就是防止同时有多个相同 URL 的请求发生。
这段代码使用 dispatch_barrier_sync 将任务放入一个并发队列,目的是在并发队列中,这个任务执行时,不允许别的任务同时执行。因为 downloadImageWithURL: 方法要返回一个遵从<SDWebImageOperation> 的对象,所以要同步执行而不能异步。
|
|
我们下面来分析这个传入的无参数的代码. 首先这段代码初始化了一个 NSMutableURLRequest:
|
|
这个 request 就用于在之后发送 HTTP 请求.
在初始化了这个 request 之后, 又初始化了一个 SDWebImageDownloaderOperation 的实例, 这个实例, 就是用于请求网络资源的操作. 它是一个 NSOperation 的子类,
|
|
在 progressBlock 的回调方法里,会通过 sself.URLCallbacks 取出这个 URL 所有 kProgressCallbackKey 的回调方法,并将获取到的 receivedSize 和 expectedSize 的值传入这些方法中调用。
在 completedBlock 的回调方法里和 progressBlock 中的一样,取出 kCompletedCallbackKey 对应的回调方法,将获取到的 image ,data,error,finished 的值传入方法中调用,还会删除 sself.URLCallbacks 中这个 URL 的回调数组 ,保障这个 URL 下次可以重新创建新请求。
在 cancelBlock 中则只是删除掉 sself.URLCallbacks 中这个 URL 的回调数组。
在往下是,给 operation 设置是否应该解压图片的属性,解压图片会提高下载和缓存的性能,但是会消耗较多的内存,如果程序因为占用内存过多而闪退则要把这个属性设置成 NO。
设置 operation 请求的 NSURLCredential ,用于在请求过程中,服务端要求验证客户端的凭证 - (void)URLSession: task: didReceiveChallenge: completionHandler: 。
再往后,是设置 NSOperation 的操作优先级。[wself.downloadQueue addOperation:operation]; 是将操作任务加到 NSOperationQueue 队列中,开始任务。最后是设置操作的执行顺序,默认是 FIFO 的先进先出的模式,也可以改成 LIFO 后进先出的栈模式,实现的方法就是添加依赖,前面的操作依赖后面的操作。设置完之后,则 return 这个 operation。
到此,SDWebImageDownloader 的这个核心方法就介绍完了。
还有一点,下载的请求是使用的 NSURLSession ,SDWebImageDownloader 将 NSURLSession 的 delegate 设置成自己,统一接收这些回调方法。在这些回调方法中,会返回一个 NSURLSessionDataTask 通过这个 dataTask 的 taskIdentifier ,我们就可以在 self.downloadQueue.operations 中找到回调方法对应的 operation (SDWebImageDownloaderOperation),每个 operation 中都有这些代理方法,这样在 SDWebImageDownloader 统一接收的回调中用找到的 operation 调用当前的这个代理方法,把参数传到对应的 operation 中。
代码如下:
|
|
SDWebImageDownloaderOperation 的 - (id)initWithRequest:(NSURLRequest *)request inSession: options: progress: completed: cancelled:; 是下载图片的关键代码,下篇文章我们就来看下SDWebImageDownloaderOperation 这个类。
相关阅读:
SDWebImage 源码阅读记录(一)
SDWebImageManager 源码阅读记录(二)
SDWebImageDownloader 源码阅读记录(三)
SDWebImageDownloaderOperation 源码阅读记录(四)
SDWebImageCache 源码阅读记录(五)
SDWebImage相关类 源码阅读记录(六)