第五篇
前言
本篇主要講解圖片緩存類的知識,雖然只涉及了圖片方面的緩存的設(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指定namespace和path.
注意:如果想設(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,path為http://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)部分。
由于個人知識有限,如有錯誤之處,還望各路大俠給予指出啊