SDWebImage源碼之SDImageCache

SDImageCache是SDWebImage庫的圖片緩存類粒梦,其提供了內存和磁盤緩存兩種機制,并且設計了一些策略對緩存的圖片進行管理钾腺。


以下代碼和分析都基于041842bf085cbb711f0d9e099e6acbf6fd533b0c這個commit瞻离。

SDImageCache

// See https://github.com/rs/SDWebImage/pull/1141 for discussion
@interface AutoPurgeCache : NSCache
@end

@implementation AutoPurgeCache

- (nonnull instancetype)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    return self;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

@end

AutoPurgeCache用作SDImageCache的內存緩存使用。雖然NSCache本身為了保證不引起內存問題力惯,實現(xiàn)了自動剔除數(shù)據(jù)的策略,但它仍然無法保證在UIApplicationDidReceiveMemoryWarningNotification發(fā)生時清空內存召嘶,詳見這里父晶。
所以AutoPurgeCache在收到UIApplicationDidReceiveMemoryWarningNotification后,調用removeAllObjects清除緩存里所有的數(shù)據(jù)弄跌。


FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
    return image.size.height * image.size.width * image.scale * image.scale;
}

- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock {
    ...
    if (self.config.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(image);
        [self.memCache setObject:image forKey:key cost:cost];
    }
    ...
}

- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost {
    self.memCache.totalCostLimit = maxMemoryCost;
}

NSCache支持設置totalCostLimit甲喝,在設置每一個數(shù)據(jù)的時候都分別設上數(shù)據(jù)的cost。緩存里的數(shù)據(jù)到達限制時會觸發(fā)NSCache的清理策略铛只,來保證數(shù)據(jù)的總cost低于限制埠胖。清理數(shù)據(jù)的具體時機和策略由NSCache內部實現(xiàn)決定,外部無法控制淳玩。
cost一般被設置為value的占用字節(jié)數(shù)直撤,但是也可以根據(jù)需求設計不同的邏輯。

- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
    UIImage *diskImage = [self diskImageForKey:key];
    if (diskImage && self.config.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }

    return diskImage;
}

最近命中的數(shù)據(jù)有較大可能會再次被用到蜕着,所以在從磁盤讀取數(shù)據(jù)后谋竖,把它也存到內存緩存。

- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                       diskCacheDirectory:(nonnull NSString *)directory {
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self                                    
        selector:@selector(backgroundDeleteOldFiles)                                     
        name:UIApplicationDidEnterBackgroundNotification
        object:nil];
   ...
}

- (void)backgroundDeleteOldFiles {
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    [self deleteOldFilesWithCompletionBlock:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}
                       

在程序進到后臺以后,開一個后臺任務清理圖片緩存蓖乘,把過期的圖片緩存從磁盤上清除锤悄。
清除策略保存在SDImageCacheConfig里,包括了過期時間和最大占用磁盤空間兩項驱敲。
具體的實現(xiàn)是先刪除所有過期的圖片铁蹈,再從舊到新刪除文件,直到滿足磁盤空間要求众眨。詳細代碼見deleteOldFilesWithCompletionBlock方法握牧。

SDImageCacheTests


// Testing storeImage:forKey:toDisk:YES
- (void)test07InsertionOfImageForcingDiskStorage{
    XCTestExpectation *expectation = [self expectationWithDescription:@"storeImage forKey toDisk=YES"];
    
    UIImage *image = [self imageForTesting];
    [self.sharedImageCache storeImage:image forKey:kImageTestKey toDisk:YES completion:nil];
    expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal(image);
    [self.sharedImageCache diskImageExistsWithKey:kImageTestKey completion:^(BOOL isInCache) {
        if (isInCache) {
            [expectation fulfill];
        } else {
            XCTFail(@"Image should be in cache");
        }
    }];
    [self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}

緩存類的測試代碼可以直接使用類本身提供的增刪改查接口進行相互驗證,這段代碼中也包括了XCTest框架中異步接口的測試寫法娩梨。


SDWebImage源碼系列

SDWebImage源碼之SDImageCache
SDWebImage源碼之SDWebImageDownloader
SDWebImage源碼之SDWebImageManager

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末沿腰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子狈定,更是在濱河造成了極大的恐慌颂龙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纽什,死亡現(xiàn)場離奇詭異措嵌,居然都是意外死亡,警方通過查閱死者的電腦和手機芦缰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門企巢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人让蕾,你說我怎么就攤上這事浪规。” “怎么了探孝?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵笋婿,是天一觀的道長。 經(jīng)常有香客問我顿颅,道長缸濒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任粱腻,我火速辦了婚禮绍填,結果婚禮上,老公的妹妹穿的比我還像新娘栖疑。我一直安慰自己,他們只是感情好滔驶,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布遇革。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萝快。 梳的紋絲不亂的頭發(fā)上锻霎,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音揪漩,去河邊找鬼旋恼。 笑死,一個胖子當著我的面吹牛奄容,可吹牛的內容都是我干的冰更。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昂勒,長吁一口氣:“原來是場噩夢啊……” “哼蜀细!你這毒婦竟也來了?” 一聲冷哼從身側響起戈盈,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤奠衔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后塘娶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體归斤,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年刁岸,在試婚紗的時候發(fā)現(xiàn)自己被綠了脏里。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡难捌,死狀恐怖膝宁,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情根吁,我是刑警寧澤员淫,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站击敌,受9級特大地震影響介返,放射性物質發(fā)生泄漏。R本人自食惡果不足惜沃斤,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一圣蝎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衡瓶,春花似錦徘公、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坦袍。三九已至,卻和暖如春等太,著一層夾襖步出監(jiān)牢的瞬間捂齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工缩抡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奠宜,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓瞻想,卻偏偏與公主長得像压真,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子内边,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容