SDWebImage的图片下载代码主要集中在SDWebImageDownloader类与SDWebImageDownloaderOperation类中。后者是真正的将图片数据从网络上下载到终端的操作,前者主要是对后者进行并发处理。
SDWebImageDownloaderOperation
它是一个NSOperation的子类,并实现了SDWebImageOperation、NSURLSessionTaskDelegate、NSURLSessionDataDelegate等协议,定义如下:
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
-(instanceType)initWithRequest:(NSURLRequest*)request
inSession:(NSURLSession*)session
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock;
@end
从初始化接口定义来看,该类会将下载过程中的回调Block保存起来并在后续的实际下载过程中触发。既然是NSOperation类的子类,那么来看下它的具体实现函数start,具体逻辑见下图:
NSURLSessionDelegate
SDWebImageDownloaderOperation主要通过实现NSURLSessionDataDelegate与NSURLSessionTaskDelegate两个协议来实现下载过程监控。
-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveData:(NSData*)data
{
//向缓存中追加刚接收到的数据
//SDWebImageDownloaderProgressiveDownload模式下根据当前接收到的图片数据绘制部分图片,调用completedBlock
//调用progressBlock,用于显示图片下载进度信息
}
-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask willCacheResponse:(NSCachedURLResponse*)proposeResponse completionHandler:(void(^)(NSCachedURLResponse* cachedResponse))completionHandler
{
//根据初始化时传入request参数确定当前图片的缓存机制
}
-(void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error
{
//发送SDWebImageDownloadStopNotification消息
//error不为空,发送SDWebImageDownloadFinishNotification
//调用completionBlock,将下载结束的具体信息传出
}
SDWebImageDownloader
该类中与图片下载有关的成员变量、成员函数如下:
@property (strong, nonatomic) NSOperationQueue* downloadQueue;
@property (strong, nonatomic) dispatch_queue_t barrierQueue;
-(id<SDWebImageOperation>)downloadImageWithURL:(NSURL*)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
downloadImageWithURL:options:progress:completed
这个函数实际上很简单,不要被它里面的几个Block定义迷惑住了。它在内部只是调用了addProgressCallback:completedBlock:forURL:createCallback:接口,然后直接返回了。addProgressCallback函数内部将传入进来的各种callbackBlock组合成一个字典中,该字典通过图片URL地址保存在SDWebImageDownloader对象的URLCallbacks变量中。代码如下:
-(void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL*)url createCallback:(SDWebImageNoParamsBlock)createCallback
{
if(url == nil){
if(completedBlock != nil){
completedBlock(nil, nil, nil, NO);
}
return;
}
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if(!self.URLCallbacks[url]){
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
}
NSMutableArray* callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary* callbacks = [NSMutableDictionary new];
if(progressBlock){
callbacks[kProgressCallbackKey] = [progressBlock copy];
}
if(completedBlock){
callbacks[kCompletedCallbackKey] = [completedBlockcopy];
}
self.URLCallbacks[url] = callbacksForURL;
if(first){
createCallback();
}
});
}
从上面代码中我们可以看到,downloadImageWithURL中传入的progressBlock与completedBlock都被缓存起来了,而createCallback则是在当前下载地址末在下载队列中时调用,主要功能就是创建SDWebImageDownloadOperation对象并添加到下载队列中。
再回到downloadImageWithURL的具体实现中,可以看到SDWebImageDownloadOperation对象创建过程中,progressBlock与completedBlock基本逻辑是从成员变量URLCallbacks中获取玩家传入的Block后调用,而completedBlock另外多的一步是判断是否将同图片URL标识的callback组合数组从URLCallbacks中删除。