SDMemoryCache
SDMemoryCache是SDImageCache類中的一個私有類,繼承自NSCache類惋嚎,它接收兩個泛型<KeyType, ObjectType>用于定義NSMapTable類型的屬性weakCache际邻。
// strong-weak cache
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache;
NSMapTable在SDWebImage學習筆記之NSMapTable中做過介紹嘲驾,weakCache屬性的初始化代碼為
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
表示weakCache變量指向的對象強引用key值展鸡,弱引用value值。假如沒有其他變量強引用value值九昧,weakCache變量將安全的刪除相應(yīng)的key-value。
SDMemoryCache還定義了一個dispatch_semaphore_t屬性weakCacheLock毕匀。
// a lock to keep the access to `weakCache` thread-safe
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock;
weakCacheLock屬性創(chuàng)建了一個初始值為1的信號量铸鹰,表示同時最多只有一個線程可以訪問資源,初始化代碼為:
self.weakCacheLock = dispatch_semaphore_create(1);
SDMemoryCache重寫了父類NSCache的三個方法:
- -(nullable ObjectType)objectForKey:(KeyType)key;
- -(void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
- -(void)removeObjectForKey:(KeyType)key;
通過對信號量weakCacheLock的控制皂岔,實現(xiàn)線程安全的weakCache賦值蹋笼、取值、刪除操作躁垛,還提供了一個清空緩存的函數(shù)
-(void)removeAllObjects剖毯。
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
return image.size.height * image.size.width * image.scale * image.scale;
#endif
// Store weak cache
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
UNLOCK(self.weakCacheLock);
// Check weak cache
LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
UNLOCK(self.weakCacheLock);
if (obj) {
// Sync cache
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
cost = SDCacheCostForImage(obj);
}
[super setObject:obj forKey:key cost:cost];
}
// Remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
UNLOCK(self.weakCacheLock);
// Manually remove should also remove weak cache
LOCK(self.weakCacheLock);
[self.weakCache removeAllObjects];
UNLOCK(self.weakCacheLock);
SDMemoryCache小結(jié)
SDMemoryCache類的兩個屬性:weakCache和weakCacheLock。weakCache用于保存數(shù)據(jù)教馆,且當數(shù)據(jù)在外部被銷毀時逊谋,weakCache可以安全的清除對應(yīng)的鍵值對;weakCacheLock用于保證線程安全活玲,同一時刻只允許只允許有一個線程對weakCache進行讀寫涣狗。
SDImageCacheConfig
SDImageCacheConfig用于SDImageCache的配置谍婉,繼承自NSObject,定義配置屬性如下:
// 是否解壓圖片
@property (assign, nonatomic) BOOL shouldDecompressImages;
// 是否禁用iCloud
@property (assign, nonatomic) BOOL shouldDisableiCloud;
// 是否使用內(nèi)存緩存镀钓,默認YES
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
// 磁盤緩存讀取選項穗熬,枚舉
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;
// 磁盤緩存寫入選項,枚舉
@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;
// 在緩存中保存圖像的最長時間丁溅,以秒為單位
@property (assign, nonatomic) NSInteger maxCacheAge;
// 緩存的最大大小唤蔗,以字節(jié)為單位
@property (assign, nonatomic) NSUInteger maxCacheSize;
// 緩存配置過期類型,枚舉
@property (assign, nonatomic) SDImageCacheConfigExpireType diskCacheExpireType;
SDImageCache
SDImageCache是Cache模塊的核心類窟赏,它提供了一系列的方法來存儲圖片妓柜,以其中最重要的存儲方法為例:
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
該方法共有6個參數(shù),image表示圖片涯穷,imageData表示圖片數(shù)據(jù)棍掐,在賦值時,代碼先判斷imageData是否為空拷况,不為空則直接將NSData類型的數(shù)據(jù)進行存儲作煌,如果imageData為空且image不為空,則先判斷UIImage類型的圖片是否存在Alpha(透明)通道赚瘦,返回一個SDImageFormat類型的枚舉值用于將UIImage轉(zhuǎn)化為NSData粟誓,然后進行存儲。
// 及時釋放圖片資源
@autoreleasepool {
NSData *data = imageData;
// 判斷imageData是否為空
if (!data && image) {
// 判斷images是否存在Alpha Channel
SDImageFormat format;
if (SDCGImageRefContainsAlpha(image.CGImage)) {
format = SDImageFormatPNG;
} else {
format = SDImageFormatJPEG;
}
// UIImage -> NSData
data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format];
}
// 存儲data
[self _storeImageDataToDisk:data forKey:key];
}
第三個參數(shù)key有兩個作用起意,一是在內(nèi)存緩存可用的前提下作為key存儲圖片鹰服,形成映射關(guān)系;二是生成磁盤緩存的URL地址揽咕,將圖片保存在該地址下悲酷,后期可以通過key獲取地址。
1.
// 內(nèi)存緩存可用
if (self.config.shouldCacheImagesInMemory) {
// 計算圖片占用空間大小
NSUInteger cost = SDCacheCostForImage(image);
// 將key和image作為鍵值對存儲在NSMapTable類型的內(nèi)存緩存中
[self.memCache setObject:image forKey:key cost:cost];
}
2.
// _storeImageDataToDisk:forkey:方法
// 使用key生成磁盤緩存地址
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// 將地址轉(zhuǎn)換成NSURL對象
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
// 存儲圖片
[imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
第四個參數(shù)toDisk用于標示是否需要將圖片存儲到磁盤心褐,需要的話才會執(zhí)行存儲的操作舔涎,第五個參數(shù)completionBlock是執(zhí)行結(jié)束的回調(diào),返回值為空逗爹。
if (toDisk) {
// 開啟新的線程執(zhí)行
dispatch_async(self.ioQueue, ^{
// 將圖片緩存到磁盤
......
if (completionBlock) {
// 返回主線程執(zhí)行
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
if (completionBlock) {
completionBlock();
}
}
SDImageCache類中定義了屬性ioQueue亡嫌,它創(chuàng)建了一個串行隊列。
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
串行隊列在執(zhí)行異步操作時掘而,會開啟一個新的線程來執(zhí)行挟冠,具體可參考下表:
同步 | 異步 | |
---|---|---|
串行隊列(主隊列) | 在主線程中執(zhí)行 | 在主線程中執(zhí)行 |
串行隊列(非主隊列) | 在當前線程中執(zhí)行 | 在新建線程中執(zhí)行 |
并發(fā)隊列 | 在當前線程中執(zhí)行 | 在新建線程中執(zhí)行 |
因此在子線程完成存儲圖片的操作后,需返回到主線程隊列執(zhí)行回調(diào)函數(shù)袍睡。
SDImageCache還提供了查詢圖片是否存在于磁盤知染、獲取磁盤圖片、查詢內(nèi)存圖片斑胜、刪除磁盤圖片控淡、刪除內(nèi)存圖片等方法嫌吠,同樣,所有的方法都會在串行隊列中異步執(zhí)行掺炭,由于串行隊列遵守FIFO(先進先出)的原則辫诅,所以可以保證只有才一個操作完成后,下一個方法才可以被執(zhí)行涧狮。
當從硬盤中獲取圖片時炕矮,代碼會判斷內(nèi)存緩存是否可用,如果可用者冤,則將圖片緩存到內(nèi)存中肤视。
// 獲取圖片
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
UIImage *diskImage = [self diskImageForKey:key];
// 判斷圖片是否存在及內(nèi)存緩存是否可用
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
return diskImage;
}
SDImageCache中定義了一個屬性customPaths,可以調(diào)用addReadOnlyCachePath:方法往customPaths數(shù)組中添加常用的磁盤緩存路徑涉枫,以便diskImageDataBySearchingAllPathsForKey:方法查找圖片資源邢滑。
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
......
NSArray<NSString *> *customPaths = [self.customPaths copy];
for (NSString *path in customPaths) {
......
}
......
}
除了對圖片的操作之外,SDImageCache還提供了內(nèi)存緩存的設(shè)置功能拜银,包括設(shè)置緩存大小和數(shù)量限制殊鞭,還提供了清理內(nèi)存緩存、清理磁盤緩存尼桶、刪除文件的方法,還有一些其他獲取圖片信息锯仪、緩存信息的方法等等泵督。
總結(jié)
SDImageCache的核心功能是對圖片的存儲、查找庶喜、刪除操作小腊。提供了兩種方法用于緩存圖片,內(nèi)存緩存和磁盤緩存久窟。
內(nèi)存指的是程序的運行空間秩冈,空間小但緩存速度快,程序一關(guān)閉斥扛,內(nèi)存就被釋放了入问,內(nèi)存分5大區(qū)域:棧區(qū)、堆區(qū)稀颁、全局區(qū)、常量區(qū)匾灶、代碼區(qū),由高地址指向低地址阶女。
磁盤指的是程序的存儲空間哩治,空間大但緩存速度慢,數(shù)據(jù)可持久化衬鱼。
iOS程序的磁盤被稱為沙盒业筏,本程序無法訪問其他應(yīng)用的沙盒,沙盒中默認有3個文件夾:Documents, Library 和 tmp驾孔。
而且所有的操作都是在串行隊列中異步執(zhí)行,即不會阻塞主線程惯疙,也保證了數(shù)據(jù)的安全性。