一行行看SDWebImage原始碼(二)

發表於2016-07-04
276769-f7b02c377c44f9ea
地表最強

最近真的真的太太太忙了,都是抽空寫的,各種事情,html,iOS客戶端升級,炸了


上一篇我們基本上看完了SDWebImage整個工作流程,下面我們具體看一下快取下載圖片中涉及到的相關的類

SDWebImageDownloader

SDWebImageManager實現下載依賴於下載器:SDWebImageDownloader,下載器負責管理下載任務,而執行下載任務是由SDWebImageDownloaderOperation操作完成

SDWebImageManager實現下載 就是呼叫下面這個方法:

我們還是先來看看SDWebImageDownloader裡面都寫了些什麼
SDWebImageDownloader.h

這些選項主要涉及到下載的優先順序,快取,後臺任務執行,cookie處理以及證書認證幾個方面,在建立下載操作的時候可以使用組合的選項來完成一些特殊的需求

定義裡兩個常量,後面通知的時候用的,這裡的常量是全域性常量

全域性常量:不管你定義在任何資料夾,外部都能訪問

區域性常量:用static修飾後,不能提供外界訪問(只能在賦值的.m檔案使用,外界不可訪問)

定義了三個block

  • 第一個返回已經接收的圖片資料的大小,未接收的圖片資料的大小,- (void)sd_setImageWithPreviousCachedImageWithURL: placeholderImage: options: progress:completed:
    這個方法裡面就有用到,因為圖片的下載是需要時間的,所以這個block回撥不止回撥一次,會一直持續到圖片完全下載或者下載失敗才會停止回撥
  • 第二個block回撥 下載完成的圖片 , 圖片的資料 , 如果有error返回error ,以及下載是否完成的BOOl值
  • 第三個是header過濾:設定一個過濾器,為下載圖片的HTTP request選取header.最終使用的headers是經過這個block過濾時候的返回值

定義的屬性

看完這些屬性後我們在來看SDWebImageDownloader裡面的兩個核心方法,其他的方法會捎帶說一下
第一個就是一開始我們說的,SDWebImageManager會呼叫的方法

先來看看-addProgressCallback:completedBlock:forURL:createCallback:裡面都做了些什麼

下面重點也是不太好理解的東西,我也是又系統地複習了一下GCD,琢磨了有段時間才繼續寫的

如果你GCD非常熟悉就跳過吧,不熟悉就先來看看我總結的GCD吧,寫的比較好理解,先來看看 幾個概念

Serial 序列 Concurrent併發
任務序列執行每次只有一個任務執行
任務併發執行就是同一時間可以有多個任務被執行

Synchronous 同步
一個同步函式只有在它完成預定的任務才返回(返回的意思是:返回當前執行緒,執行緒繼續向下執行任務,你可以自己做個測試用一個同步函式,任務裡面sleep(3);測試一下就明白了)

Asynchronous 非同步
一個非同步函式,會立即返回,預定任務會完成,但是不會等到這個任務完成才返回

Queues 佇列
GCD提供 dispatch queues來處理程式碼,這些佇列管理你提供給GCD的任務並用FIFO順序執行,這保證了第一個被新增到佇列裡的任務會是佇列中第一個執行的,第二個被新增的任務第二個開始執行,如此直到佇列的終點
只能保證任務開始的順序不能保證任務結束的順序

Serial Queues 序列佇列
序列佇列的任務一次執行一個,每一個任務只有在前一個任務完成的時候才開始,但是你不知道一個任務(block)和下一個開始之間的時間長度

Concurrent Queues 併發佇列
在併發佇列中的任務能得到的保證是它們會被按照被新增的順序開始執行,任務能以任意順序完成,但是你不知道什麼時候才開始執行下一個任務,或者任意時刻有多少block在執行,這完全取決於GCD

Queue Type 佇列型別
主佇列(main queue),和其它序列佇列一樣,這個佇列中的任務一次只能執行一個,然後它能保證所有的任務都在主執行緒執行,而主執行緒是唯一可用於更新UI的執行緒,這個佇列就是用於發訊息給UIView或傳送通知的

全域性排程佇列(Global Dispatch Queues),它分了四種優先順序(任務執行的優先順序):background , low , default , high
Apple的API也會使用這些佇列,所以你新增的任何任務都不會是這些佇列唯一的任務

自己建立的序列佇列 或者併發佇列

GCD提供的函式
dispatch_async 非同步 , 與其他執行緒無關
dispatch_sync 同步,阻塞其他執行緒
dispatch_apply 重複執行
dispatch_after 延遲執行
dispatch_barrier_async dispatch_barrier_sync(下面細講)

只列舉了一些常用的GCD函式,並不完全

GCD的使用呢,總結起來就是先選用一個GCD提供的函式,傳入一個你要呼叫的佇列(三種佇列型別的一種)和一個block(任務),
佇列會在輪到這個block執行的時候執行這個block

注意:佇列是用來存放任務的,佇列並不等於執行緒,佇列中存放的任務最後都要由執行緒來執行

再回到剛才要看的部分,dispatch_barrier_sync是我們選用的GCD提供的函式,self.barrierQueue是存放任務的佇列,block裡面是要執行的任務

先來看看dispatch_barrier_sync
Dispatch Barrier解決多執行緒併發讀寫一個資源發生死鎖
sync說明了這是個同步函式,任務不會立即返回,會等到任務執行結束才返回
使用dispatch_barrier_sync此函式建立的任務會首先去檢視佇列中有沒有別的任務要執行,如果有則會等待已有任務執行完畢再執行;同時在此方法後新增的任務必須等到此方法中任務執行後才能執行,利用這個方法可以控制執行順序
Dispatch Barrier確保提交的block是指定佇列中特定時段唯一在執行的一個.在所有先於Dispatch Barrier的任務都完成的情況下這個block才開始執行.輪到這個block時barrier會執行這個block並且確保佇列在此過程 不會執行其他任務.block完成後才恢復佇列

這是使用者自己建立的佇列,DISPATCH_QUEUE_CONCURRENT代表的是它是一個並行佇列,為什麼選擇併發佇列而不是序列佇列我們來想一下:
序列佇列可以保證任務按照新增的順序一個個開始執行,並且上一個任務結束才開始下一個任務,這已經可以保證任務的執行順序(或者說是任務結束的順利)了,但是並行佇列不一樣,併發佇列只能保證任務的開始,至於任務以什麼樣的順序結束並不能保證但是併發佇列使用Barrier卻是可以保證的

這部分就先到這裡繼續向下看:

URLCallbacks是一個可變字典,key是NSURL型別,value為NSMutableArray型別,value(陣列裡面)只包含一個元素,這個元素的型別是NSMutableDictionary型別,這個字典的key為NSString型別代表著回撥型別,value為block,是對應的回撥
這些程式碼的目的都是為了給url繫結回撥

繼續向下看:

如果url第一次繫結它的回撥,也就是第一次使用這個url建立下載任務則執行一次建立回撥
在建立回撥中 建立下載操作(下載操作並不是在這裡建立的),dispatch_barrier_sync執行確保同一時間只有一個執行緒操作URLCallbacks屬性,也就是確保了下面建立過程中在給operation傳遞迴調的時候能取到正確的self.URLCallbacks[url]值,同事確保後面有相同的url再次建立的時候if (!self.URLCallbacks[url])分支不再進入,first==NO,也就不再繼續呼叫建立回撥,這樣就確保了同一個url對應的圖片不會重複下載

以上這部分程式碼總結起來只做了一件事情:在barrierQueue佇列中建立下載任務
至此下載的任務都建立好了,下面該輪到下載的操作了:

NSURLCredential 身份認證

認證過程
1.web伺服器接收到來自客戶端的請求
2.web服務並不直接返回資料,而是要求客戶端提供認證資訊,也就是說挑戰是服務端向客戶端發起的
2.1要求客戶端提供使用者名稱與密碼挑戰 NSInternetPassword
2.2 要求客戶端提供客戶端證書 NSClientCertificate
2.3要求客戶端信任該伺服器
3.客戶端回撥執行,接收到需要提供認證資訊,然後提供認證資訊,並再次傳送給web服務
4.web服務驗證認證資訊
4.1認證成功,將最終的資料結果傳送給客戶端
4.2認證失敗,錯誤此次請求,返回錯誤碼401


Web服務需要驗證客戶端網路請求
NSURLConnectionDelegate 提供的接收挑戰,SDWeImage使用的就是這個方案

至此下載管理 SDWebImageDownloader到這裡就算結束了,它的主要作用就是建立下載任務,管理下載任務(取消,下載等狀態改變)這裡的重點就是對self.barrierQueue的理解,最後我們來看看SDWebImageDownloaderOptions下載操作和下載過程的實現


SDWebImageDownloaderOptions

它的作用就是網路請求的配置,進行網路請求以及資料處理
依舊先來看看它公開宣告的屬性和方法

然後繼續看SDWebImageDownloaderOperation.h

初始化方法,這個就是初始化一個SDWebImageDownloaderOperation例項,沒什麼看點

但是下面這個方法- (void)start就是關鍵了,它是對NSOperation- (void)start的重寫,這個方法是執行下載任務的核心程式碼

最後,我們來看NSURLConnection (delegate)
1.connection: didReceiveResponse:

2.connection: didReceiveData拼接資料的協議

3.connectionDidFinishLoading:這個方法完成以後,代理不再會接收人和connection傳送的訊息,標誌著圖片下載完成,一般下載任務正常結束之後就會執行一次這個方法

到這裡,看的也差不多了,認真看完感覺這個作者太厲害了,也真的學習到了很多,歡迎交流,也希望大家自己有空了也看一下,這次真的是拖了一個月因為有的東西我沒明白就看了好多天也查了 各種資料,這次也算是盡力寫好了吧,慚愧

包廂裡的狂歡,曲終人散
have Fine
以上

相關文章