SDWebImage學習筆記之SDImageCache

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的三個方法:

  1. -(nullable ObjectType)objectForKey:(KeyType)key;
  2. -(void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
  3. -(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ù)的安全性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霉颠,一起剝皮案震驚了整個濱河市对碌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒿偎,老刑警劉巖朽们,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诉位,死亡現(xiàn)場離奇詭異,居然都是意外死亡苍糠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門岳瞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞳筏,你說我怎么就攤上這事∫唬” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵钻心,是天一觀的道長。 經(jīng)常有香客問我捷沸,道長,這世上最難降的妖魔是什么痒给? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任骏全,我火速辦了婚禮尼斧,結(jié)果婚禮上姜贡,老公的妹妹穿的比我還像新娘棺棵。我一直安慰自己,他們只是感情好烛恤,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缚柏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪币喧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天杀餐,我揣著相機與錄音,去河邊找鬼史翘。 笑死,一個胖子當著我的面吹牛恶座,可吹牛的內(nèi)容都是我干的沥阳。 我是一名探鬼主播跨琳,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桐罕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了功炮?” 一聲冷哼從身側(cè)響起溅潜,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤薪伏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嫁怀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體借浊,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡萝招,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年蚂斤,在試婚紗的時候發(fā)現(xiàn)自己被綠了槐沼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡岗钩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凹嘲,到底是詐尸還是另有隱情,我是刑警寧澤周蹭,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站凶朗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棚愤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一宛畦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧次和,春花似錦、人聲如沸踏施。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至日熬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背定铜。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留揣炕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓畸陡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丁恭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容