篇2:SDWebImage源碼看圖片緩存

導語:這是SDWebImage源碼理解的第二篇,本篇介紹SDWebImage中的緩存相關的內容

一、概述

在SDWebImage中處理圖片緩存的是SDImageCache類。

1、SDImageCache的由來
  • 通過圖片數據(數據可能來自磁盤,也可能來自網絡) 創建 UIImage(imageWithData:) 時,底層調用 ImageIO 的 CGImageSourceCreateWithData() 方法,在 64 位的設備上默認開啟緩存(ShouldCache為YES);在 CGImage 內部,緩存了第一次渲染到屏幕上的解碼位圖數據;只要image對象不釋放,該image對象再次顯示到屏幕時,不再需要解碼;如果這個圖片被釋放掉,其內部的解碼數據也會被立刻釋放。

  • 在SDWebImage中,從網絡上下載的圖片,或從磁盤中取出圖片,會在子線程中解碼圖片,解碼后得到位圖圖片數據,不被UIImage緩存,需要SDWebImage自己去緩存。

2、SDImageCache簡述
  • SDImageCache中緩存有兩個部分,一部分是內存緩存,通過NSCache實現,以Key-Value的形式存儲圖片,當內存不夠的時候會清除所有緩存圖片;一部分是磁盤緩存,通過File System實現,在退出到后臺和應用將終止情形下,清除過期的圖片(默認保存時間大于一周的圖片)。

  • SDWebImageManager向SDImageCache請求圖片資源時,先搜索內存緩存圖片,有就直接返回;沒有的話去從磁盤讀取圖片,然后解碼圖片,并將解碼結果保存在內存緩存中;如果內存和磁盤中都沒有,才去網絡請求數據。

  • SDWebImageDownloader從網絡中下載圖片,將圖片解碼結果緩存到內存緩存中,將圖片數據保存在磁盤中。

二、內存緩存圖片

SDImageCache中負責內存緩存的是NSCache的子類AutoPurgeCache。

1、NSCache簡介
  • NSCache是蘋果官方提供的緩存類,是線程安全的,在多線程中無需對NSCache進行加鎖。NSCache的key只做強引用,無需實現NSCopying協議,不會像NSDictionary一樣復制對象。

  • NSCache的屬性有三個,分別是:totalCostLimit(最大cost限制)默認值是 0,表示無限制; countLimit(最大個數限制)默認值是 0,表示無限制;evictsObjectsWithDiscardedContent(是否回收廢棄的內容),默認值是 YES,表示自動回收。

  • NSCache的方法有存、取、刪除等方法,在SDImageCache中緩存圖片時,需要設置對應圖片對應的cost(setObject:forKey:cost:), 緩存里的數據量到達限制時,會觸發NSCache的清理策略,具體清除策略由NSCache內部實現,外部無法控制。

    //SDImageCache中,在iphone 設備上,單個圖片的cost計算公式如下:
    cost = imageHeight * imageWidth * imageScale * imageScale;
    
  • NSCacheDelegate只有一個方法,將要刪除對象時調用(cache:willEvictObject),該方法中不能修改緩存中內容。

2、AutoPurgeCache的設計

具體代碼如下:

@interface AutoPurgeCache : NSCache
@end

@implementation AutoPurgeCache
- (nonnull instancetype)init {
    self = [super init];
    if (self) {
#if SD_UIKIT
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects)   name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
    }
    return self;
}

- (void)dealloc {
#if SD_UIKIT
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
}
@end

說明:因為NSCache在iOS7系統中不會響應內存告警,SDWebImage中就子類化了NSCache,自己監聽內存告警,并removeAllObjects。

3、內存緩存的清除策略
  • 當內存不足,收到內存警告(UIApplicationDidReceiveMemoryWarningNotification)時,會清除內存緩存中所有數據(100%實現)。

  • 內存緩存可以設置countLimit和 totalCostLimit(默認沒設置);當緩存對象個數超過countLimit,或者cost之和超過totalCostLimit,cache中的對象會觸發清除機制,但是具體的清除時機和策略依賴于cache的實現細節。

三、磁盤緩存圖片

1、保存圖片到磁盤
  • 將 UIImage 保存到磁盤的方式有三種:1)直接用 NSKeyedArchiver 把 UIImage 序列化保存; 2)用 UIImagePNGRepresentation() 先把圖片轉為 PNG 保存; 3) **UIImageJPEGRepresentation() **把圖片壓縮成 JPEG 保存。

  • 其中,NSKeyedArchiver 是調用了 UIImagePNGRepresentation() 進行序列化的,用它來保存圖片是消耗最大的。蘋果對 JPEG 有硬編碼和硬解碼,保存成 JPEG 會大大縮減編碼解碼時間,也能減小文件體積。如果圖片不包含透明像素時,**UIImageJPEGRepresentation(0.9) **是比較好的圖片保存方式,其次是 UIImagePNGRepresentation()。

  • 從網絡中下載下來的是圖片數據(imageData), 只有在SDWebImageDownloaderScaleDownLargeImages情形下,圖片壓縮后,使用UIImagePNGRepresentation()進行序列化,更新imageData,再保存到磁盤,其他情況下,直接保存網絡下載下來的imageData到磁盤。

2、從磁盤中讀取圖片
  • SDWebImageManager向SDImageCache請求圖片資源時,內存緩存中沒有圖片,需要從磁盤中讀取圖片數據(diskImageDataBySearchingAllPathsForKey:),然后解碼圖片,并將解碼結果保存在內存緩存中;如果內存和磁盤中都沒有,才去網絡請求數下載圖片。

  • imageData轉成image對象的工作由UIImage (MultiFormat)中的sd_imageWithData:完成,其中利用sd_imageOrientationFromImageData:來根據imageData數據獲取圖片類型。利用sd_imageOrientationFromImageData:獲取圖片的旋轉方向。

    + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data {
      if (!data) {
          return nil;
      }
    
      UIImage *image;
      SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
      if (imageFormat == SDImageFormatGIF) {
          image = [UIImage sd_animatedGIFWithData:data];
      }
      #ifdef SD_WEBP
      else if (imageFormat == SDImageFormatWebP)
      {
          image = [UIImage sd_imageWithWebPData:data];
      }
      #endif
      else {
          image = [[UIImage alloc] initWithData:data];
      #if SD_UIKIT || SD_WATCH
          UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
          if (orientation != UIImageOrientationUp) {
              image = [UIImage imageWithCGImage:image.CGImage
                                      scale:image.scale
                                orientation:orientation];
          }
      #endif
          }
      return image;
    }
    
3、磁盤緩存的清除策略
  • 每次下載下來的圖片數據,默認會保存在磁盤中;時間久了,如果不去清除就的圖片數據,就會浪費磁盤空間,所以SDWebImage在 App退出到后臺、應用即將被終止 的時候會執行清除工作。主要是清除所有過期(保存時間超過maxCacheAge)的圖片,如果還未到達磁盤空間要求(大于maxCacheSize),再從舊到新刪除文件,直到滿足。最大緩存時間(maxCacheAge)和最大占用磁盤空間(maxCacheSize)在 SDImageCacheConfig類設置。

  • App退出到后臺清除緩存需要注意:此時需要調用UIApplication的beginBackgroundTaskWithExpirationHandler:方法,來向iOS系統點時間,處理清除過期圖片這個耗時較長的任務,在任務結束后需要調用endBackgroundTask:方法,并標志任務結束,否則程序會被iOS系統終止。

    - (void)backgroundDeleteOldFiles {
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
          return;
      }
      UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
      __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
          // Clean up any unfinished task business by marking where you
          // stopped or ending the task outright.
          [application endBackgroundTask:bgTask];
          bgTask = UIBackgroundTaskInvalid;
      }];
    
      // Start the long-running task and return immediately.
      [self deleteOldFilesWithCompletionBlock:^{
          [application endBackgroundTask:bgTask];
          bgTask = UIBackgroundTaskInvalid;
      }];
    }
    

End

  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容