SDWebImage源碼解讀之SDWebImageCache(上)

第五篇

前言

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

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

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

整體架構(gòu)

我們不看實現(xiàn)文件,只看作者暴露出來的內(nèi)容,來分析該類有哪些屬性和方法??赐?code>整體架構(gòu)這一節(jié),我們必須明白如何使用這個緩存管理者。具體的實現(xiàn)過程會在下邊的實現(xiàn)原理一節(jié)中講解。

1.緩存位置

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

  • 內(nèi)存
  • 硬盤

2.配置

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

  • shouldDecompressImages 是否解壓縮圖片,默認為YES
  • disable iCloud backup 是否禁用iCloud備份, 默認為YES
  • shouldCacheImagesInMemory 是否緩存到內(nèi)存中,默認為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.內(nèi)存最大緩存

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

4.最大內(nèi)存緩存數(shù)量

可以通過maxMemoryCountLimit來設(shè)置內(nèi)存的最大緩存數(shù)量是多少。

5.初始化

一般來說,一個管理類都有一個全局的單利對象,該類也不例外,然后根據(jù)業(yè)務(wù)需求設(shè)計不同的初始化方法。不管是什么樣的類,我們在設(shè)計它的時候,應(yīng)該通過合理的初始化方法告訴別的開發(fā)者,該類應(yīng)該如何創(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.

注意:如果想設(shè)置某個方法為指定的初始化方法,通過NS_DESIGNATED_INITIALIZER來實現(xiàn)。

6.Cache paths

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

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

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

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

我在想,一個好的架構(gòu),或框架,應(yīng)該使用這用思想

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

/**
 * 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.存儲圖片

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

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

代碼如下:

/**
 * 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.獲取圖片

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

  • 判斷圖片是否被緩存到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,原因是在內(nèi)存中獲取耗時非常短,在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;
    
  • 同步在內(nèi)存查詢圖片

      /**
       * 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;
    
  • 同步查找圖片,先內(nèi)存后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.移除某條數(shù)據(jù)

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

  • 全部移除

      /**
       * 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;
    
  • 移除內(nèi)存數(shù)據(jù),是否也移除disk數(shù)據(jù)

      /**
       * 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數(shù)據(jù),是否也移除內(nèi)存數(shù)據(jù) 這種情況SDWebImageCache未實現(xiàn)

10.移除數(shù)據(jù)

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

  • 清空內(nèi)存

      /**
       * 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;
    
  • 清空過期數(shù)據(jù)

      /**
       * 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.獲取緩存相關(guān)信息

獲取緩存的相關(guān)信息:

  • 獲取disk使用size

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

      /**
       * 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,那么調(diào)用后邊的方法后,返回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;
    

總結(jié)

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

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

  1. SDWebImage源碼解讀 之 NSData+ImageContentType 簡書 博客園
  2. SDWebImage源碼解讀 之 UIImage+GIF 簡書 博客園
  3. SDWebImage源碼解讀 之 SDWebImageCompat 簡書 博客園
  4. SDWebImage源碼解讀_之SDWebImageDecoder 簡書 博客園
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容