面試時如何優雅的談論原始碼

發表於2016-11-04

當前的大環境比去年差了很多,一方面求職的人多了而崗位因為經濟形勢的影響卻相應的少了很多。通貨膨脹,生活壓力只增不減。對於應聘的萬千大軍而言,如何能脫穎而出,是個值得考慮的技術問題。自己的價值在於不可替代性或是難以替代性。如果,隨便找個程式設計師就能把你replace掉,你的價值就很低廉了。如果在你負責的某個方面,只有20%的人超越你,那你的價值、你的重要性就凸顯出來了,你與僱主的關係就從被動轉向了主動,你就有了談判的籌碼。在專業化高度分工的今天,一技之長並不是說需要你掌握某個很大的方面,而只需要你能掌握其中的某一個小的領域,並不斷地深入下去。思考原始碼將是一個很好的切入點。

本文主要涉及一下幾點:

  • SDWebImage 原理
  • SDWebImage 使用
  • SDWebImage 原始碼分析
  • 一些思考

SDWebImage 載入圖片原理

具體原始碼分析見GIT。

111170656-6971e44291c18bb4
目前標註的類

通過標註的類,看懂應該沒問題了。

SDWebImage是一個圖片快取的框架。相較於AFNetworking整合的UIImageView+AFNetworking.h,對於圖片的快取實際應用的是NSURLCache自帶的cache機制。而NSURLCache每次都要把快取的raw data 再轉化為UIImage,就帶來了資料處理和記憶體方面的更多操作。SDWebImage的快取由SDImageCache類來實現,這是一個單例類,該類負責處理記憶體快取及一個可選的磁碟快取,其中磁碟快取的寫操作是非同步的,這樣就不會對UI操作造成影響。此外還提供了若干屬性和介面來配置和操作快取物件。包含以下功能:

1.提供UIImageView的一個分類,以支援網路圖片的載入與快取管理

2.一個非同步的圖片載入器

3.一個非同步的記憶體+磁碟圖片快取

4.支援GIF圖片

5.支援WebP圖片

6.後臺圖片解壓縮處理

7.確保同一個URL的圖片不被下載多次

8.確保虛假的URL不會被反覆載入

9.確保下載及快取時,主執行緒不被阻塞

SDWebImage底層實現原理:

SDWebImage有沙盒快取機制,主要由三塊組成

1.記憶體圖片快取

2.記憶體操作快取

3.磁碟沙盒快取

SDWebImage的大部分工作是由快取物件SDImageCache和非同步下載器管理物件SDWebImageManager來完成的。SDWebImage的圖片下載是由SDWebImageDownloader這個類來實現的,它是一個非同步下載管理器,下載過程中增加了對圖片載入做了優化的處理。而真正實現圖片下載的是自定義的一個Operation操作,將該操作加入到下載管理器的操作佇列downloadQueue中,Operation操作依賴系統提供的NSURLConnection類實現圖片的下載。

SDWebImage提供了對圖片快取的支援,而該功能是由SDImageCache類來完成的。該類負責處理記憶體快取及一個可選的磁碟快取。記憶體快取的處理是使用NSCache物件來實現的。NSCache是一個類似於集合的容器。它儲存key-value對,這一點類似於NSDictionary類,用搜尋檔案系統的方式做管理,檔案替換方式是以時間為單位。我們通常用使用快取來臨時儲存短時間使用但建立昂貴的物件。重用這些物件可以優化效能,因為它們的值不需要重新計算。另外一方面,這些物件對於程式來說不是緊要的,在記憶體緊張時會被丟棄。

磁碟快取的處理則是使用NSFileManager物件來實現的。圖片儲存的位置是位於Cache資料夾。另外,SDImageCache還定義了一個序列佇列,來非同步儲存圖片。

當SDWebImageManager向SDImageCache要資源時,先搜尋記憶體層面的資料,如果有直接返回,沒有的話去訪問磁碟,將圖片從磁碟讀取出來,然後做Decoder,將圖片物件放到記憶體層面做備份,再返回撥用層。使用Decoder 是因為UIImage的imageWithData函式是每次畫圖的時候才將Data解壓成ARGB的影像,
所以在每次畫圖的時候,會有一個解壓操作,這樣效率很低,但是隻有瞬時的記憶體需求。
為了提高效率通過SDWebImageDecoder將包裝在Data下的資源解壓,然後畫在另外一張圖片上,這樣這張新圖片就不再需要重複解壓了。是典型的空間換時間的做法。

121170656-8c5e61bb0b11d4e0

SDWebImage的原理

1.使用

會先把 placeholderImage 顯示,然後 SDWebImageManager 根據 URL 開始處理圖片。

2.進入 SDWebImageManager

交給 SDImageCache 從快取查詢圖片是否已經下載。

3.先從記憶體圖片快取查詢是否有圖片,如果記憶體中已經有圖片快取,取快取,沒有從- (UIImage )diskImageForKey:(NSString )key去磁碟快取中去查詢,根據 URLKey 在硬碟快取目錄下嘗試讀取圖片檔案。在磁碟快取中找到後,同時更新置記憶體快取中(如果空閒記憶體過小,會先清空記憶體快取),有回撥則呼叫doneBlock回撥。

4.找到了就從SDWebImageQueryCompletedBlock到 UIImageView+WebCache 等前端展示圖片。

5.如果從硬碟快取目錄讀取不到圖片,說明不存在該圖片,需要下載圖片,共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片。 圖片下載由 NSURLSession 來做,實現相關 delegate 來判斷圖片下載中、下載完成和下載失敗。

6.URLSession:didReceiveData: 中利用 ImageIO 做了按圖片下載進度載入效果。資料下載完成後交給 SDWebImageDecoder 做圖片解碼處理。

7.圖片解碼處理在一個 NSOperationQueue 完成,不會拖慢主執行緒 UI。如果有需要對下載的圖片進行二次處理,最好也在這裡完成,效率會好很多。

8.在主執行緒 SDWebImageDownloaderCompletedBlock裡處理解碼完成後的操作。回撥給需要的地方展示圖片。

9.從SDWebImageDownloaderProgressBlock 回撥給 SDWebImageManager 告知圖片下載資訊。

10.將圖片儲存到 SDImageCache 中,記憶體快取和硬碟快取同時儲存。寫檔案到硬碟也在以單獨 NSInvocationOperation 完成,避免拖慢主執行緒。

11.SDImageCache 在初始化的時候會註冊一些訊息通知,在記憶體警告或退到後臺的時候清理記憶體圖片快取,應用結束的時候清理過期圖片。

12.SDWebImage 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache方便使用。 SDWebImagePrefetcher 可以預先下載圖片,方便後續使用。

131170656-bf7c86e7bd12b9c7

SDWebImage 使用

常用到的物件:

1、UIImageView (WebCache)類別,入口封裝,實現讀取圖片完成後的回撥。

2、SDWebImageManager,對圖片進行管理的中轉站,記錄那些圖片正在讀取。
向下層讀取Cache(呼叫SDImageCache),或者向網路讀取物件(呼叫SDWebImageDownloader) 。
實現SDImageCache和SDWebImageDownloader的回撥。

3、SDImageCache,根據URL的MD5摘要對圖片進行儲存和讀取(實現存在記憶體中或者存在硬碟上兩種實現)
實現圖片和記憶體清理工作。

4、SDWebImageDownloader,根據URL向網路讀取資料(實現部分讀取和全部讀取後再通知回撥兩種方式)

5、SDWebImageDecoder,非同步對影像進行了一次解壓

使用:

呼叫setImageWithURL:方法的時候,SDWebImage自動做很多事,當你需要在某一具體時刻做事情的時候,你可以覆蓋這些方法。比如在下載某個圖片的過程中要響應一個事件,就覆蓋這個方法:

基本程式碼:

使用SDWebImageManager類:可以進行一些非同步載入的工作。

當然你的類要實現SDWebImageManagerDelegate協議,並且要實現協議的webImageManager:didFinishWithImage:方法。

獨立的非同步影像下載

可能會單獨用到非同步圖片下載,則一定要用downloaderWithURL:delegate:來建立一個SDWebImageDownloader例項。

這樣SDWebImageDownloaderDelegate協議的方法imageDownloader:didFinishWithImage:被呼叫時下載會立即開始並完成。

獨立的非同步影像快取

SDImageCache類提供一個建立空快取的例項,並用方法imageForKey:來尋找當前快取。

儲存一個影像到快取是使用方法storeImage: forKey:

預設情況下,影像將被儲存在記憶體快取和磁碟快取中。如果僅僅是想記憶體快取中,要使用storeImage:forKey:toDisk:方法的第三個引數帶一負值
來替代。

SDWebImage 原始碼分析示例

SDWebImageDownloader類

SDWebImageDownloaderOptions定義:

下載順序:

每個下載操作都定義了回撥操作,如下載進度回撥,下載完成回撥,頭部過濾等,這些回撥操作是以block形式來呈現;每個下載操作的下載進度回撥和下載完成回撥,這兩個回撥稍後將儲存在下載管理器的URLCallbacks字典中,key為URL,value為一個陣列,陣列裡面又存放一個儲存了下載進度回撥和完成回撥程式碼塊的字典。這個字典陣列同時也保證了同一張圖片只會被下載一次。

為了保證URLCallbacks操作(新增、刪除)的執行緒安全性,SDWebImageDownloader將這些操作作為一個個任務放到barrierQueue佇列中,並設定屏障來確保同一時間只有一個執行緒操作URLCallbacks屬性。

下載請求的管理都是放在downloadImageWithURL:options:progress:completed:方法裡面來處理的,該方法呼叫了上面所提到的addProgressCallback:andCompletedBlock:forURL:createCallback:方法來將請求的資訊存入管理器中,同時在建立回撥的block中建立新的操作,配置之後將其放入downloadQueue操作佇列中,最後方法返回新建立的操作。

下載操作的超時時間可以通過downloadTimeout屬性來設定,預設值為15秒。

SDWebImage定義了一個協議,即 SDWebImageOperation 作為圖片下載操作的基礎協議。它只宣告瞭一個cancel方法,用於取消操作。每個圖片的下載都是一個Operation操作。SDWebImage自定義了一個Operation類,即 SDWebImageDownloaderOperation ,它繼承自NSOperation,並採用了SDWebImageOperation協議。除了繼承而來的方法,該類只向外暴露了一個方法,initWithRequest:options:progress:completed:cancelled:。對於圖片的下載,SDWebImageDownloaderOperation完全依賴於URL載入系統中的NSURLSession。具體看程式碼原始碼分析.

方法的主要任務是接收資料。每次接收到資料時,都會用現有的資料建立一個CGImageSourceRef物件以做處理。在首次獲取到資料時(width+height==0)會從這些包含影像資訊的資料中取出影像的長、寬、方向等資訊以備使用。而後在圖片下載完成之前,會使用CGImageSourceRef物件建立一個圖片物件,經過縮放、解壓縮操作後生成一個UIImage物件供完成回撥使用。當然,在這個方法中還需要處理的就是進度資訊。如果我們有設定進度回撥的話,就呼叫這個進度回撥以處理當前圖片的下載進度。

縮放操作可以檢視SDWebImageCompat檔案中的SDScaledImageForKey函式;解壓縮操作可以檢視SDWebImageDecoder檔案+decodedImageWithImage方法。在下載完成或下載失敗後,需要停止當前執行緒的run loop,清除連線,並丟擲下載停止的通知。如果下載成功,則會處理完整的圖片資料,對其進行適當的縮放與解壓縮操作,以提供給完成回撥使用。

最重要是自己分析,看過的會忘,消化了才是自己的。學而不思則罔,思而不學則殆。每個人要了解自己的優缺點。有思考才有所得!

一些思考

我們應該把精力和時間投入在更值得學習的東西上。如果純看程式碼而沒有碰到這個場景就算看懂了也沒法理解,學習的目的是為了實踐,而不要為了原理而分析原理,這樣就本末倒置了。對於廣大程式設計師而言,做碼農,通過低水平重複的勞動來創造價值的道路是永遠不可能一勞永逸的,恰恰相反,是永勞一逸的!生產只能夠惠及當下!事物的發展在於思考,可能每個人走的路徑不一樣,尋找到最適合自己的,堅持走下去。只要能肯定每天都是進步的,快慢又有什麼關係呢。這是一個浮躁的社會,這個社會催生了無數可能,也許不是每個人都能做一輩子的程式設計師,但不要辜負你的時光。你之所以成為你,是因為你的時間,你的經歷在哪裡,你就在哪裡。共勉!

相關文章