SDWebImage源碼初探(二)

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;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贩幻,隨后出現(xiàn)的幾起案子轿腺,更是在濱河造成了極大的恐慌,老刑警劉巖丛楚,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件族壳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡趣些,警方通過(guò)查閱死者的電腦和手機(jī)仿荆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)坏平,“玉大人拢操,你說(shuō)我怎么就攤上這事〔疤妫” “怎么了令境?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坎穿。 經(jīng)常有香客問(wèn)我展父,道長(zhǎng),這世上最難降的妖魔是什么玲昧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任栖茉,我火速辦了婚禮,結(jié)果婚禮上孵延,老公的妹妹穿的比我還像新娘吕漂。我一直安慰自己,他們只是感情好尘应,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布惶凝。 她就那樣靜靜地躺著,像睡著了一般犬钢。 火紅的嫁衣襯著肌膚如雪苍鲜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天玷犹,我揣著相機(jī)與錄音混滔,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坯屿,可吹牛的內(nèi)容都是我干的油湖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼领跛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乏德!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吠昭,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤喊括,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后怎诫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘾晃,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年幻妓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蹦误。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肉津,死狀恐怖强胰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妹沙,我是刑警寧澤偶洋,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站距糖,受9級(jí)特大地震影響玄窝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悍引,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一恩脂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趣斤,春花似錦俩块、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至联贩,卻和暖如春漫仆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泪幌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工盲厌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玄渗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓狸眼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親浴滴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拓萌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容