SDWebImageDownloader
上一篇寫(xiě)了一下sd_setImageWithURL:這個(gè)系列方法的大致實(shí)現(xiàn)流程,這一篇來(lái)詳細(xì)看一看下載圖片時(shí)使用的下載器類-SDWebImageDownloader稚配。
讓我們先從頭文件開(kāi)始看起:
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
SDWebImageDownloaderLowPriority = 1 << 0,
//低優(yōu)先級(jí)的下載
SDWebImageDownloaderProgressiveDownload = 1 << 1,
//逐漸下載勾缭,可以實(shí)現(xiàn)圖片一點(diǎn)點(diǎn)被加載出來(lái)的效果
SDWebImageDownloaderUseNSURLCache = 1 << 2,
//開(kāi)啟NSURLCache妹懒,默認(rèn)情況不開(kāi)啟
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
//查找到緩存圖片直接返回空值,與SDWebImageDownloaderUseNSURLCache結(jié)合使用
SDWebImageDownloaderContinueInBackground = 1 << 4,
//后臺(tái)下載設(shè)置,如果后臺(tái)app的持續(xù)時(shí)間到了时鸵,這個(gè)下載任務(wù)也會(huì)被取消亚侠。
SDWebImageDownloaderHandleCookies = 1 << 5,
//通過(guò)設(shè)置NSMutableURLRequest.HTTPShouldHandleCookies = YES的方式來(lái)管理那些儲(chǔ)存在NSHTTPCookieStore中的cookies
SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
//允許不受信任的SSL證書(shū)曹体,測(cè)試環(huán)境下很有效,在生產(chǎn)環(huán)境中需要謹(jǐn)慎使用
SDWebImageDownloaderHighPriority = 1 << 7,
//高優(yōu)先級(jí)下載
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
SDWebImageDownloaderFIFOExecutionOrder,
//先進(jìn)先出
SDWebImageDownloaderLIFOExecutionOrder
//后進(jìn)先出
};
對(duì)上面這個(gè)枚舉硝烂,我認(rèn)為是針對(duì)整個(gè)下載隊(duì)列的下載順序箕别,而不是某個(gè)下載任務(wù)的屬性。
extern NSString *const SDWebImageDownloadStartNotification;
extern NSString *const SDWebImageDownloadStopNotification;
//這里使用const修飾符來(lái)定義常量滞谢,官方也推薦這樣使用而不是#define串稀,[不了解的同學(xué)點(diǎn)這里。](http://www.reibang.com/p/f83335e036b5)狮杨。
typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
//使用漸進(jìn)下載時(shí)回調(diào)的Block receivedSize:已接收的圖片數(shù)據(jù)大心附亍;expectedSize:未接收的圖片數(shù)據(jù)大小
typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished);
//下載完成時(shí)回調(diào)的Block image:下載的圖片橄教;data:圖片的Data清寇;error:下載失敗時(shí)的錯(cuò)誤信息;finished:是否下載完成护蝶。
typedef NSDictionary *(^SDWebImageDownloaderHeadersFilterBlock)(NSURL *url, NSDictionary *headers);
//一個(gè)Header過(guò)濾器华烟,會(huì)返回過(guò)濾之后的headers。
@property (assign, nonatomic) BOOL shouldDecompressImages;
//解壓下載后的圖片持灰,缺省值是YES盔夜,圖片過(guò)大可能會(huì)擠爆內(nèi)存,根據(jù)實(shí)際情況設(shè)置。
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
//最大并發(fā)下載數(shù)喂链,在SDWebImagePrefetcher中被定義為3返十。
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
//當(dāng)前并發(fā)下載數(shù)
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
//下載超時(shí)時(shí)間,缺省值為15.0衩藤。
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
//下載隊(duì)列的執(zhí)行順序吧慢,默認(rèn)為先進(jìn)先出
+ (SDWebImageDownloader *)sharedDownloader;
//返回一個(gè)全局的單例下載器對(duì)象
@property (strong, nonatomic) NSURLCredential *urlCredential;
//為請(qǐng)求設(shè)置默認(rèn)的URL憑據(jù)
/**
* Set username
*/
@property (strong, nonatomic) NSString *username;
/**
* Set password
*/
@property (strong, nonatomic) NSString *password;
@property (nonatomic, copy) SDWebImageDownloaderHeadersFilterBlock headersFilter;
//給下載圖片的請(qǐng)求過(guò)濾header,返回過(guò)濾后的header
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
//給每個(gè)HTTP的header設(shè)置請(qǐng)求頭文件赏表,傳入nil來(lái)取消header
- (NSString *)valueForHTTPHeaderField:(NSString *)field;
//獲取HTTPHeader的請(qǐng)求頭文件
- (void)setOperationClass:(Class)operationClass;
//給SDWebImageDownloaderOperation設(shè)置默認(rèn)子類检诗。傳入nil恢復(fù)SDWebImageDownloaderOperation。
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock;
//這個(gè)就是核心方法了瓢剿,用來(lái)下載指定URL的圖片逢慌。下面會(huì)詳細(xì)分析
- (void)setSuspended:(BOOL)suspended;
//控制下載隊(duì)列暫停與否的屬性
/**
* Cancels all download operations in the queue
*/
- (void)cancelAllDownloads;
下面進(jìn)入.m文件查看downloadImageWithURL:options:progress:completed:方法的實(shí)現(xiàn)過(guò)程:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
__block SDWebImageDownloaderOperation *operation;
__weak __typeof(self)wself = self;
[self addProgressCallback:progressBlock
completedBlock:completedBlock forURL:url createCallback:^{....}];
//block中省略號(hào)是收起的代碼,未展現(xiàn)间狂。由此可見(jiàn)攻泼,這個(gè)下載圖片的方法基本又在addProgressCallback:completedBlock:forURL:createCallback:這個(gè)方法中所實(shí)現(xiàn)。
return operation;
}
我們進(jìn)入addProgressCallback:completedBlock:forURL:createCallback:看看它的實(shí)現(xiàn)過(guò)程:
- (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) {
//URL是作為callbacks(一個(gè)字典)的key值鉴象,所以不可以為空忙菠,如果為空則直接返回。
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return;
}
dispatch_barrier_sync(self.barrierQueue, ^{
//這個(gè)函數(shù)最近正好在書(shū)里看到過(guò)纺弊,是一種在并發(fā)隊(duì)列中插入一小段阻塞當(dāng)前線程的串行隊(duì)列方法牛欢。
//即前有A、B淆游、C方法并發(fā)傍睹,中間有dispatch_barrier_sync,其后還有D犹菱、E拾稳、F并發(fā),程序會(huì)先并發(fā)ABC腊脱、然后執(zhí)行dispatch_barrier_sync中的方法访得,并且阻塞當(dāng)前線程,執(zhí)行過(guò)后再并發(fā)DEF陕凹。
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
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
//上面的代碼可以分析為:通過(guò)上層方法的block響應(yīng)來(lái)給url綁定block回調(diào)震鹉。
if (first) {
createCallback();
//如果是第一次綁定,則創(chuàng)建下載回調(diào)捆姜,去創(chuàng)建下載任務(wù)。
//此時(shí)還在dispatch_barrier_sync中迎膜,可以保證該URL不會(huì)被重復(fù)創(chuàng)建下載任務(wù)泥技。
}
});
}
下面來(lái)看看createCallback里面的執(zhí)行過(guò)程:
NSTimeInterval timeoutInterval = wself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
//這里設(shè)置下載的超時(shí)時(shí)間
// 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];
//為了避免雙重緩存,要判斷是否設(shè)置了SDImageCache,來(lái)設(shè)置NSURLCache的開(kāi)關(guān)珊豹。
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
//設(shè)置請(qǐng)求項(xiàng)
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
//設(shè)置請(qǐng)求頭文件
operation = [[wself.operationClass alloc] initWithRequest:request
inSession:self.session
options:options
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
dispatch_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
});
//取出URL對(duì)應(yīng)的block數(shù)組
for (NSDictionary *callbacks in callbacksForURL) {
dispatch_async(dispatch_get_main_queue(), ^{
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
if (callback) callback(receivedSize, expectedSize);
//回傳進(jìn)度參數(shù)簸呈。
});
}
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
__block NSArray *callbacksForURL;
·· dispatch_barrier_sync(sself.barrierQueue, ^{
callbacksForURL = [sself.URLCallbacks[url] copy];
if (finished) {
[sself.URLCallbacks removeObjectForKey:url];
//如果任務(wù)完成了,將其從URLCallbacks中刪除店茶。
}
});
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
//回傳下載成功的image
}
}
cancelled:^{
SDWebImageDownloader *sself = wself;
if (!sself) return;
dispatch_barrier_async(sself.barrierQueue, ^{
[sself.URLCallbacks removeObjectForKey:url];
//取消下載時(shí)將URL對(duì)應(yīng)的所有Block移除蜕便。
});
}];
operation.shouldDecompressImages = wself.shouldDecompressImages;
//設(shè)置解壓選項(xiàng)
if (wself.username && wself.password) {
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
//用戶認(rèn)證設(shè)置
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
//設(shè)置下載優(yōu)先級(jí)
[wself.downloadQueue addOperation:operation];
//加入下載任務(wù)到下載隊(duì)列中
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}
//設(shè)置下載隊(duì)列中的下載順序
}];
return operation;