SDWebImage源碼解讀之SDWebImageCache(上)

第五篇

前言

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

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

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

整體架構

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

1.緩存位置

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

  • 內存
  • 硬盤

2.配置

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

  • shouldDecompressImages 是否解壓縮圖片净薛,默認為YES
  • disable iCloud backup 是否禁用iCloud備份, 默認為YES
  • shouldCacheImagesInMemory 是否緩存到內存中蒲拉,默認為YES
  • maxCacheAge 最大的緩存不過期時間肃拜, 單位為秒痴腌,默認為一周的時間
  • maxCacheSize 最大的緩存尺寸,單位為字節(jié)

代碼如下:

@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.初始化

一般來說猛蔽,一個管理類都有一個全局的單利對象剥悟,該類也不例外,然后根據業(yè)務需求設計不同的初始化方法曼库。不管是什么樣的類区岗,我們在設計它的時候,應該通過合理的初始化方法告訴別的開發(fā)者毁枯,該類應該如何創(chuàng)建

  • + (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;

注意:在開發(fā)中赂韵,我們會遇到這樣的情況教藻,假如我之前把圖片緩存到了地址1,現在我打算重構代碼右锨。寫了這么一個緩存管理者括堤,我需要和之前的緩存的圖片建立聯(lián)系,但是以后都打算使用新寫的這個管理者绍移,那怎么辦呢悄窃??

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

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

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

/**
 * 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 簡書 博客園
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坎怪,隨后出現的幾起案子坐昙,更是在濱河造成了極大的恐慌,老刑警劉巖芋忿,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炸客,死亡現場離奇詭異疾棵,居然都是意外死亡,警方通過查閱死者的電腦和手機痹仙,發(fā)現死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門是尔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人开仰,你說我怎么就攤上這事拟枚。” “怎么了众弓?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵恩溅,是天一觀的道長。 經常有香客問我谓娃,道長脚乡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任滨达,我火速辦了婚禮奶稠,結果婚禮上,老公的妹妹穿的比我還像新娘捡遍。我一直安慰自己锌订,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布画株。 她就那樣靜靜地躺著辆飘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谓传。 梳的紋絲不亂的頭發(fā)上劈猪,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音良拼,去河邊找鬼战得。 笑死,一個胖子當著我的面吹牛庸推,可吹牛的內容都是我干的常侦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼贬媒,長吁一口氣:“原來是場噩夢啊……” “哼聋亡!你這毒婦竟也來了?” 一聲冷哼從身側響起际乘,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤坡倔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體罪塔,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡投蝉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了征堪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘩缆。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖佃蚜,靈堂內的尸體忽然破棺而出庸娱,到底是詐尸還是另有隱情,我是刑警寧澤谐算,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布熟尉,位于F島的核電站,受9級特大地震影響洲脂,放射性物質發(fā)生泄漏斤儿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一腮考、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玄捕,春花似錦踩蔚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馍迄,卻和暖如春福也,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背攀圈。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工暴凑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赘来。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓现喳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親犬辰。 傳聞我的和親對象是個殘疾皇子嗦篱,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容