SDWebImage源碼解讀之SDWebImageCache(上)

第五篇

前言

本篇主要講解圖片緩存類的知識,雖然只涉及了圖片方面的緩存的設計,但思想同樣適用于別的方面的設計。在架構上來說,緩存算是存儲設計的一部分。我們把各種不同的存儲內容按照功能進行切割后,圖片緩存便是其中的一個。

我們在封裝自己的圖片緩存管理對象的時候,SDWebImageCache能夠提供大約90%的代碼給我們直接使用,基于這些代碼,我們需要分析出作者的設計思想是什么?當需要緩存某個列表時,基于SDWebImageCache的設計思想,我們就能夠設計出比較合理的緩存管理對象了。

所謂舉一反三就是這樣的道理。

整體架構

我們不看實現文件,只看作者暴露出來的內容,來分析該類有哪些屬性和方法。看完整體架構這一節,我們必須明白如何使用這個緩存管理者。具體的實現過程會在下邊的實現原理一節中講解。

1.緩存位置

圖片可以被緩存到兩個地方:

  • 內存
  • 硬盤

2.配置

通過SDImageCacheConfig這個類來管理緩存的配置信息,我們打開SDImageCacheConfig后,發現可以配置的東西有:

  • shouldDecompressImages 是否解壓縮圖片,默認為YES
  • disable iCloud backup 是否禁用iCloud備份, 默認為YES
  • shouldCacheImagesInMemory 是否緩存到內存中,默認為YES
  • maxCacheAge 最大的緩存不過期時間, 單位為秒,默認為一周的時間
  • maxCacheSize 最大的緩存尺寸,單位為字節

代碼如下:

@interface SDImageCacheConfig : NSObject

/**
 * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
 * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
 */
@property (assign, nonatomic) BOOL shouldDecompressImages;

/**
 *  disable iCloud backup [defaults to YES]
 */
@property (assign, nonatomic) BOOL shouldDisableiCloud;

/**
 * use memory cache [defaults to YES]
 */
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;

/**
 * The maximum length of time to keep an image in the cache, in seconds
 */
@property (assign, nonatomic) NSInteger maxCacheAge;

/**
 * The maximum size of the cache, in bytes.
 */
@property (assign, nonatomic) NSUInteger maxCacheSize;

@end

--

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week

@implementation SDImageCacheConfig

- (instancetype)init {
    if (self = [super init]) {
        _shouldDecompressImages = YES;
        _shouldDisableiCloud = YES;
        _shouldCacheImagesInMemory = YES;
        _maxCacheAge = kDefaultCacheMaxCacheAge;
        _maxCacheSize = 0;
    }
    return self;
}

@end

3.內存最大緩存

可以通過maxMemoryCost來設置內存的最大緩存是多少,這個是以像素為單位的。

4.最大內存緩存數量

可以通過maxMemoryCountLimit來設置內存的最大緩存數量是多少。

5.初始化

一般來說,一個管理類都有一個全局的單利對象,該類也不例外,然后根據業務需求設計不同的初始化方法。不管是什么樣的類,我們在設計它的時候,應該通過合理的初始化方法告訴別的開發者,該類應該如何創建

  • + (nonnull instancetype)sharedImageCache 單利
  • - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns 通過制定的namespace來初始化
  • - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER 指定namespacepath.

注意:如果想設置某個方法為指定的初始化方法,通過NS_DESIGNATED_INITIALIZER來實現。

6.Cache paths

既然把數據緩存到了disk中,那么就要提供一個方法獲取這個緩存路徑。這里通過下邊這個方法,根據namespace獲取緩存路徑:

- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;

注意:在開發中,我們會遇到這樣的情況,假如我之前把圖片緩存到了地址1,現在我打算重構代碼。寫了這么一個緩存管理者,我需要和之前的緩存的圖片建立聯系,但是以后都打算使用新寫的這個管理者,那怎么辦呢??

我們想到,我只需要把之前的路徑添加到管理類的路徑集合中就行了。主要目的是在搜索圖片的時候,也有權限去搜索新添加的路徑。

我在想,一個好的架構,或框架,應該使用這用思想

這也是下邊這個方法的意義:

/**
 * Add a read-only cache path to search for images pre-cached by SDImageCache
 * Useful if you want to bundle pre-loaded images with your app
 *
 * @param path The path to use for this read-only cache path
 */
- (void)addReadOnlyCachePath:(nonnull NSString *)path;

7.存儲圖片

我們已經說過了,圖片會被存儲到內存或者硬盤中,在這一存儲過程的設計中有下邊這幾個需要考慮的因素:

  • 數據源:可以保存UIImage也可以保存NSData
  • 唯一標識:找到該數據的唯一標識,一般使用圖片的URL
  • 是否需要保存到硬盤:根據配置文件中的設置,如果設置了應該緩存到內存,那么圖片肯定會被緩存到內存中。
  • 數據保存這一過程必須是異步的,在完成之后,在主線程回調

代碼如下:

/**
 * Asynchronously store an image into memory and disk cache at the given key.
 *
 * @param image           The image to store
 * @param key             The unique image cache key, usually it's image absolute URL
 * @param completionBlock A block executed after the operation is finished
 */
- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;

/**
 * Asynchronously store an image into memory and disk cache at the given key.
 *
 * @param image           The image to store
 * @param key             The unique image cache key, usually it's image absolute URL
 * @param toDisk          Store the image to disk cache if YES
 * @param completionBlock A block executed after the operation is finished
 */
- (void)storeImage:(nullable UIImage *)image
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;

/**
 * Asynchronously store an image into memory and disk cache at the given key.
 *
 * @param image           The image to store
 * @param imageData       The image data as returned by the server, this representation will be used for disk storage
 *                        instead of converting the given image object into a storable/compressed image format in order
 *                        to save quality and CPU
 * @param key             The unique image cache key, usually it's image absolute URL
 * @param toDisk          Store the image to disk cache if YES
 * @param completionBlock A block executed after the operation is finished
 */
- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock;

/**
 * Synchronously store image NSData into disk cache at the given key.
 *
 * @warning This method is synchronous, make sure to call it from the ioQueue
 *
 * @param imageData  The image data to store
 * @param key        The unique image cache key, usually it's image absolute URL
 */
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

8.獲取圖片

對于如何獲取圖片。作者給出了比較多的方式,首先考慮內存和硬盤,其次考慮異步獲取還是同步獲取。如果獲取數據異步的,就要使用block。總結下來有這么幾種情況:

  • 判斷圖片是否被緩存到disk(異步)

      /**
       *  Async check if image exists in disk cache already (does not load the image)
       *
       *  @param key             the key describing the url
       *  @param completionBlock the block to be executed when the check is done.
       *  @note the completion block will be always executed on the main queue
       */
      - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
    
  • 異步查詢圖片是否存在,這里返回了一個NSOperation,原因是在內存中獲取耗時非常短,在disk中時間相對較長。

      /**
       * Operation that queries the cache asynchronously and call the completion when done.
       *
       * @param key       The unique key used to store the wanted image
       * @param doneBlock The completion block. Will not get called if the operation is cancelled
       *
       * @return a NSOperation instance containing the cache op
       */
      - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
    
  • 同步在內存查詢圖片

      /**
       * Query the memory cache synchronously.
       *
       * @param key The unique key used to store the image
       */
      - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
    
  • 同步在disk查詢圖片

      /**
       * Query the disk cache synchronously.
       *
       * @param key The unique key used to store the image
       */
      - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
    
  • 同步查找圖片,先內存后disk

      /**
       * Query the cache (memory and or disk) synchronously after checking the memory cache.
       *
       * @param key The unique key used to store the image
       */
      - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
    

9.移除某條數據

數據可能存在于內存,也可能是disk,也可能兩者都有,那么我們要想移除數據,就要考慮這些情況了。

  • 全部移除

      /**
       * Remove the image from memory and disk cache asynchronously
       *
       * @param key             The unique image cache key
       * @param completion      A block that should be executed after the image has been removed (optional)
       */
      - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
    
  • 移除內存數據,是否也移除disk數據

      /**
       * Remove the image from memory and optionally disk cache asynchronously
       *
       * @param key             The unique image cache key
       * @param fromDisk        Also remove cache entry from disk if YES
       * @param completion      A block that should be executed after the image has been removed (optional)
       */
      - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
    
  • 移除disk數據,是否也移除內存數據 這種情況SDWebImageCache未實現

10.移除數據

這個移除不同于上邊的移除,它會清空所有的符合條件的數據。

  • 清空內存

      /**
       * Clear all memory cached images
       */
      - (void)clearMemory;
    
  • 清空disk

      /**
       * Async clear all disk cached images. Non-blocking method - returns immediately.
       * @param completion    A block that should be executed after cache expiration completes (optional)
       */
      - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
    
  • 清空過期數據

      /**
       * Async remove all expired cached image from disk. Non-blocking method - returns immediately.
       * @param completionBlock A block that should be executed after cache expiration completes (optional)
       */
      - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
    

11.獲取緩存相關信息

獲取緩存的相關信息:

  • 獲取disk使用size

      /**
       * Get the size used by the disk cache
       */
      - (NSUInteger)getSize;
    
  • 獲取disk緩存的圖片數目

      /**
       * Get the number of images in the disk cache
       */
      - (NSUInteger)getDiskCount;
    
  • 異步獲取disk使用size

      /**
       * Asynchronously calculate the disk cache's size.
       */
      - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
    
  • 獲取某個路徑下的指定的圖片,比如key為http://www.123.com/image.png,pathhttp://www.456.com,那么調用后邊的方法后,返回http://www.456.com/image.png

      /**
       *  Get the cache path for a certain key (needs the cache path root folder)
       *
       *  @param key  the key (can be obtained from url using cacheKeyForURL)
       *  @param path the cache path root folder
       *
       *  @return the cache path
       */
      - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
    
  • 獲取默認的緩存路徑

      /**
       *  Get the default cache path for a certain key
       *
       *  @param key the key (can be obtained from url using cacheKeyForURL)
       *
       *  @return the default cache path
       */
      - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;
    

總結

本來打算把實現部分也寫到這篇文章的,但是現在看來不太合適,文章太長了,影響閱讀體驗。閱讀完本篇后,我們就能夠明白SDWebImageCache究竟能夠給我提供哪些功能,更進一步,我們了解到設計這樣一個管理者的答題思路是什么。下一篇就是該管理者的實現部分。

由于個人知識有限,如有錯誤之處,還望各路大俠給予指出啊

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

推薦閱讀更多精彩內容