这一篇我们来解决SDWebimage中下载图片的类SDWebImageDownloader,这个类学习完以后,我们对SDWebimage的理解会更加深刻,废话少说开始。
按照惯例我们先看SDWebImageDownloader.h文件
(1)SDWebImageDownloaderOptions
SDWebImageDownloaderLowPriority
SDWebImageDownloaderProgressiveDownload
SDWebImageDownloaderUseNSURLCache
SDWebImageDownloaderIgnoreCachedResponse
SDWebImageDownloaderContinueInBackground
SDWebImageDownloaderHandleCookies
SDWebImageDownloaderAllowInvalidSSLCertificates
SDWebImageDownloaderHighPriority
枚举SDWebImageDownloaderExecutionOrder
SDWebImageDownloaderFIFOExecutionOrder
SDWebImageDownloaderLIFOExecutionOrder
成员属性
shouldDecompressImages是否进行解压
maxConcurrentDownloads线程的最大并发量
currentDownloadCount当前正在下载的数量
downloadTimeout 超时
executionOrder 执行方式FIFO、LIFO
urlCredential
… …
核心方法
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
//暂停
- (void)setSuspended:(BOOL)suspended;
在.m文件中我们主要解决核行代码的实现问题
先看.m中的属性:
NSOperationQueue *downloadQueue下载队列
NSOperation *lastAddedOperation最后一个添加的队列
NSMutableDictionary *URLCallbacks回调的block的字典
NSMutableDictionary *HTTPHeaders下载图的请求头
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
dispatch_queue_t barrierQueue
核心代码
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
//创建operation
__block SDWebImageDownloaderOperation *operation;
然后调用方法对progressBlock和completedBlock进行处理
我们先看看这个方法是怎么实现的
......
}
这个方法主要是对progressBlock和completedBlock的处理工作,
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback {
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return;
}
//同步执行self.barrierQueue队列中的任务,执行完毕后调用block中的方法,判读是否是第一次下载这个url
//如果是第一次下载这个url,也就是在URLCallbacks这个字典中通过url这个key获取不到数组,那么就为这个key创建一个数组,并标记为第一次下载
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
}
// Handle single download of simultaneous download request for the same URL
//根据url从 self.URLCallbacks这个字典中取出数组,将传入的block 根据 两个key(kProgressCallbackKey和kCompletedCallbackKey),copy到字典中 然后将字典加入到
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
//将下载进度的progressBlock添加在callbacks字典中
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
//将完成的completedBlock添加在callbacks字典中
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
//将callbacks字典添加到callbacksForURL数组中
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
//如果是第一次下载就调用createCallback这个block
if (first) {
createCallback();
}
});
}
这个方法有两个作用,第一个是将progressBlock和completedBlock放在字典中,然后放入数组中,最后在放入字典中。第二个是判断是否是第一次下载这个url,如果是第一次下载那么就调用block去下载图片(如果这个url不是第一次下载,那么在前面的从内存中取或从硬盘中都能取到image,也就到不了这一步了)。
调用的这个block中,会有下载图片的方法,我们来看看这个block的实现。
在这个block中代码有很多,我们分块来看这个block的实现原理。
第一部分创建一个下载的request
//第一次下载回调用这个block中的代码
//图片下载的超时时间
NSTimeInterval timeoutInterval = wself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
//创建请求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
//HTTPShouldHandleCookies为yes 表示将cookies一起发送至服务器
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
// HTTPShouldUsePipelining表示receiver(理解为iOS客户端)的下一个信息是否必须等到上一个请求回复才能发送。
// 如果为YES表示可以,NO表示必须等receiver收到先前的回复才能发送下个信息
request.HTTPShouldUsePipelining = YES;
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
第二部分调用了SDWebImageDownloaderOperation类中的
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock
方法来下载图片。下一篇我们会重点来介绍这个类中的详细代码。
第三部分 主要是challenge、优先级、添加到队列的操作
//是否压缩图片
operation.shouldDecompressImages = wself.shouldDecompressImages;
// web 服务可以在返回 http 响应时附带认证要求的challenge,作用是询问 http 请求的发起方是谁,这时发起方应提供正确的用户名和密码(即认证信息),然后 web 服务才会返回真正的 http 响应。
//
// 收到认证要求时,NSURLConnection 的委托对象会收到相应的消息并得到一个 NSURLAuthenticationChallenge 实例。该实例的发送方遵守 NSURLAuthenticationChallengeSender 协议。为了继续收到真实的数据,需要向该发送方向发回一个 NSURLCredential 实例。
if (wself.urlCredential) {
operation.credential = wself.urlCredential;
} else if (wself.username && wself.password) {
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
//优先级
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
//将operation加入到队列中
[wself.downloadQueue addOperation:operation];
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
//如果为LIFO(栈模式)需要添加依赖关系 栈顶完成才能执行下一个operation
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}
总结:这一部分的主要是对SDWebImageDownloader类中代码的解析,我发现还是没有到下载的地方,这个类中进行主要是对各种block的处理和创建了一个request下载请求,创建了一个SDWebImageDownloaderOperation对象,并将operation添加到队列中指定了出入队列的方法等。
下一篇我们将重点解析下载,并对下载结果的处理。