SDWebImage 源碼解讀之緩存類SDImageCache

本章開始將介紹SDWebImage庫中圖片緩存策略類SDImageCache暴氏,首先解釋一下涉及到的一些基本概念和方法:

?NSCache

我們在對一個APP數(shù)據(jù)做存儲和內(nèi)存優(yōu)化的時候,不可避免的需要對緩存做相應(yīng)的處理,而且緩存處理的優(yōu)劣,往往也是決定一個APP能否長線發(fā)展的重要因素之一。NSCache是蘋果官方提供的一套緩存機制,主要作用于內(nèi)存緩存的管理方面,NSCache在系統(tǒng)內(nèi)存很低時宴树,會自動釋放一些對象(出自蘋果官方文檔缘圈,不過在模擬器中模擬內(nèi)存警告時穿香,不會做緩存的清理動作) 為了確保接收到內(nèi)存警告時能夠真正釋放內(nèi)存茧吊,最好調(diào)用一下removeAllObjects方法朽合。在沒有引入NSCache之前,我們要管理緩存,都是使用的NSMutableDictionary來管理,然而,使用NSMutableDictionary來管理緩存是有些不妥的, 知道多線程操作原理的開發(fā)者都明白, NSMutableDictionary在線程方面來說是不安全,這也是蘋果官方文檔明確說明了的,而如果使用的是NSCache,那就不會出現(xiàn)這些問題。

NSCache和NSMutableDictionary的相同點與區(qū)別:

相同點:NSCache和NSMutableDictionary功能用法基本是相同的饱狂。

區(qū)別:

1.NSCache是線程安全的,NSMutableDictionary線程不安全

2.當(dāng)內(nèi)存不足時NSCache會自動釋放內(nèi)存(所以從緩存中取數(shù)據(jù)的時候總要判斷是否為空)

3.NSCache可以指定緩存的限額宪彩,當(dāng)緩存超出限額自動釋放內(nèi)存休讳。

NSCache屬性和方法介紹:

1)屬性介紹

name:名稱

delegete:設(shè)置代理

totalCostLimit:緩存空間的最大總成本,超出上限會自動回收對象尿孔。默認(rèn)值為0俊柔,表示沒有限制

countLimit:能夠緩存的對象的最大數(shù)量。默認(rèn)值為0活合,表示沒有限制

evictsObjectsWithDiscardedContent:標(biāo)識緩存是否回收廢棄的內(nèi)容

2)方法介紹

- (void)setObject:(ObjectType)obj forKey:(KeyType)key;//在緩存中設(shè)置指定鍵名對應(yīng)的值雏婶,0成本

- (void)setObject:(ObjectType)obj forKey:(KeyType)keycost:(NSUInteger)g;

//在緩存中設(shè)置指定鍵名對應(yīng)的值,并且指定該鍵值對的成本白指,用于計算記錄在緩存中的所有對象的總成本

//當(dāng)出現(xiàn)內(nèi)存警告或者超出緩存總成本上限的時候留晚,緩存會開啟一個回收過程,刪除部分元素

- (void)removeObjectForKey:(KeyType)key;//刪除緩存中指定鍵名的對象

- (void)removeAllObjects;//刪除緩存中所有的對象

代碼示例:

?MD5加密

MD5加密是最常用的加密方法之一告嘲,是從一段字符串中通過相應(yīng)特征生成一段32位的數(shù)字字母混合碼错维。對輸入信息生成唯一的128位散列值(32個字符)奖地。軟件開發(fā)過程中,對數(shù)據(jù)進行加密是保證數(shù)據(jù)安全的重要手段赋焕,常見的加密有Base64加密和MD5加密参歹。Base64加密是可逆的,MD5加密目前來說一般是不可逆的隆判。MD5生成的是固定的128bit犬庇,即128個0和1的二進制位,而在實際應(yīng)用開發(fā)中侨嘀,通常是以16進制輸出的臭挽,所以正好就是32位的16進制,說白了也就是32個16進制的數(shù)字飒炎。MD5主要特點是 不可逆埋哟,相同數(shù)據(jù)的MD5值肯定一樣,不同數(shù)據(jù)的MD5值不一樣郎汪。MD5算法具有以下性質(zhì):

1赤赊、壓縮性:任意長度的數(shù)據(jù),算出的MD5值長度都是固定的煞赢。

2抛计、容易計算:從原數(shù)據(jù)計算出MD5值很容易。

3照筑、抗修改性:對原數(shù)據(jù)進行任何改動吹截,哪怕只修改1個字節(jié),所得到的MD5值都有很大區(qū)別凝危。

4波俄、弱抗碰撞:已知原數(shù)據(jù)和其MD5值,想找到一個具有相同MD5值的數(shù)據(jù)(即偽造數(shù)據(jù))是非常困難的蛾默。

5懦铺、強抗碰撞:想找到兩個不同的數(shù)據(jù),使它們具有相同的MD5值支鸡,是非常困難的冬念。

6、MD5加密是不可解密的牧挣,但是網(wǎng)上有一些解析MD5的急前,那個相當(dāng)于一個大型的數(shù)據(jù)庫,通過匹配MD5去找到原密碼瀑构。只要在要加密的字符串前面加上一些字母數(shù)字符號或者多次MD5加密裆针,這樣出來的結(jié)果一般是解析不出來的。下面舉例講一下MD5的使用:

#import@interface MD5Encrypt : NSObject

// MD5加密

/*由于MD5加密是不可逆的,多用來進行驗證*/

// 32位小寫

+(NSString *)MD5ForLower32Bate:(NSString *)str;

// 32位大寫

+(NSString *)MD5ForUpper32Bate:(NSString *)str;

// 16為大寫

+(NSString *)MD5ForUpper16Bate:(NSString *)str;

// 16位小寫

+(NSString *)MD5ForLower16Bate:(NSString *)str;

@end

#import "MD5Encrypt.h"

#import <CommonCrypto/CommonDigest.h>

@implementation MD5Encrypt

#pragma mark - 32位 小寫

+(NSString *)MD5ForLower32Bate:(NSString *)str{

//要進行UTF8的轉(zhuǎn)碼

const char* input = [str UTF8String];

unsigned char result[CC_MD5_DIGEST_LENGTH];

CC_MD5(input, (CC_LONG)strlen(input), result);

NSMutableString *digest = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];

for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {

[digest appendFormat:@"%02x", result[i]];

}

return digest;

}

#pragma mark - 32位 大寫

+(NSString *)MD5ForUpper32Bate:(NSString *)str{

//要進行UTF8的轉(zhuǎn)碼

const char* input = [str UTF8String];

unsigned char result[CC_MD5_DIGEST_LENGTH];

CC_MD5(input, (CC_LONG)strlen(input), result);

NSMutableString *digest = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];

for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {

[digest appendFormat:@"%02X", result[i]];

}

return digest;

}

#pragma mark - 16位 大寫

+(NSString *)MD5ForUpper16Bate:(NSString *)str{

NSString *md5Str = [self MD5ForUpper32Bate:str];

NSString *string;

for (int i=0; i<24; i++) {

string=[md5Str substringWithRange:NSMakeRange(8, 16)];

}

return string;

}

#pragma mark - 16位 小寫

+(NSString *)MD5ForLower16Bate:(NSString *)str{

NSString *md5Str = [self MD5ForLower32Bate:str];

NSString *string;

for (int i=0; i<24; i++) {

string=[md5Str substringWithRange:NSMakeRange(8, 16)];

}

return string;

}

@end

關(guān)于解讀SDWebImage庫中圖片緩存策略類SDImageCache.h的實現(xiàn)思路,我們可以借助下面這張路線圖片去理解:

一.SDImageCache緩存策略之初始化


二.SDImageCache緩存策略之查詢

SDImageCache查詢操作的具體實現(xiàn)是在下面的方法中進行的:

該方法有兩個參數(shù)值据块,一個是key码邻,一個是doneBlock,前者是作為圖片資源的唯一標(biāo)識符另假,不管是內(nèi)存緩存機制中的NSCache還是本地存儲的文件數(shù)據(jù)像屋,都是以該key值為準(zhǔn)去獲取數(shù)據(jù)。doneBlock參數(shù)則是向上一級傳遞當(dāng)前查詢結(jié)果的操作边篮。在查詢操作的一開始階段己莺,獲取圖片的數(shù)據(jù)會使用- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key方法先從內(nèi)存數(shù)據(jù)中進行查看,如果內(nèi)存中已經(jīng)存在了該圖片數(shù)據(jù)則直接返回給上一級戈轿,如果并未發(fā)現(xiàn)資源凌受,則會創(chuàng)建一個NSOperation對象,去異步的執(zhí)行從本地保存的數(shù)據(jù)中查詢是否有該數(shù)據(jù)思杯,如有則將數(shù)據(jù)引入到內(nèi)存中胜蛉,并在主線程執(zhí)行doneBlock回調(diào)操作,將結(jié)果告知上一級色乾,同時釋放資源誊册。單純只是查詢本地文件是否存在的話則使用的是下面的方法:

三.SDImageCache緩存策略之刪除

SDImageCache刪除操作的具體實現(xiàn)是在下面的方法中進行的:

前面三個方法都是基于第四個方法進行實現(xiàn)的,現(xiàn)在主要來看看第四個方法的實現(xiàn)暖璧,該方法中有三個參數(shù)中案怯,一個是key,一個是fromDisk澎办,一個是completion嘲碱,key值的作用之前已經(jīng)介紹過了,這里就不在進行說明局蚀,fromDisk參數(shù)是一個bool值麦锯,該值的作用是去判斷在刪除內(nèi)存中的數(shù)據(jù)時需不需要同時刪除本地的緩存數(shù)據(jù),completion則是完成操作的回調(diào)琅绅,向上一級傳遞當(dāng)前刪除操作的結(jié)果离咐。在該方法中,刪除操作行為都會刪除內(nèi)存中的圖片數(shù)據(jù)(除非有特定設(shè)置)奉件,之后根據(jù)需要異步去刪除本地緩存中的圖片數(shù)據(jù),并在主線程中告之上一級結(jié)果昆著。

四.SDImageCache緩存策略之保存

SDImageCache保存操作的具體實現(xiàn)是在下面的方法中進行的:

中間兩個方法都是基于第一個方法進行實現(xiàn)的县貌,現(xiàn)在主要來看看第一個方法的實現(xiàn),該方法中有五個參數(shù)中凑懂,一個是image煤痕,一個是recalculate,一個是imageData,一個是key摆碉,最后一個是toDisk塘匣。image和imageData,都是圖片的數(shù)據(jù)巷帝,只不過一個以UIImage類型忌卤,一個是NSData類型,recalculate該值的作用是去判斷圖片的二進制數(shù)據(jù)有沒有進行圖片加密處理楞泼,toDisk則是判斷當(dāng)前圖片數(shù)據(jù)是否要保存到本地驰徊。在進行保存操作是,會先將圖片數(shù)據(jù)保存到內(nèi)存中堕阔,以下代碼就是為了實現(xiàn)該目的:

if (self.shouldCacheImagesInMemory) {

NSUInteger cost = SDCacheCostForImage(image);

[self.memCache setObject:image forKey:key cost:cost];

}

圖片數(shù)據(jù)被保存到內(nèi)存中之后棍厂,接下來會異步進行保存到本地的操作,在保存到本地的操作是超陆,先對圖片的類型進行判斷牺弹,需要注意的是SDWebImage庫中也提供了,對圖片類型的判斷方法时呀,但是這些方法針對的是NSData類型的判斷张漂,并不是UIImage類型的判斷,這里需要區(qū)分一下退唠,而對于UIImage類型的判斷作者提供了一下的方式去判斷:

int alphaInfo = CGImageGetAlphaInfo(image.CGImage);

BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||

alphaInfo == kCGImageAlphaNoneSkipFirst ||

alphaInfo == kCGImageAlphaNoneSkipLast);

BOOL imageIsPng = hasAlpha;

// But if we have an image data, we will look at the preffix

if ([imageData length] >= [kPNGSignatureData length]) {

imageIsPng = ImageDataHasPNGPreffix(imageData);

}

if (imageIsPng) {

data = UIImagePNGRepresentation(image);

}

else {

data = UIImageJPEGRepresentation(image, (CGFloat)1.0);

}

判斷的依據(jù)有兩個:

1.PNG有一個獨特的簽名鹃锈,前8個字節(jié)總是包含137 80 78 71 13 10 26 10

2.PNG圖片存在一個透明度屬性值,JPG圖片沒有透明的背景瞧预,而PNG圖像可以保留透明的背景

滿足這兩個條件中的其中一個屎债,就可以判定該圖片為PNG,之后根據(jù)對象的方法UIImagePNGRepresentation(image)/UIImageJPEGRepresentation(image, (CGFloat)1.0)將圖片對象UIImage轉(zhuǎn)化成二進制數(shù)據(jù)并調(diào)用上面圖片的第四個方法將數(shù)據(jù)保存到本地指定的路徑中垢油。

五.SDImageCache緩存策略之讀取

獲取本地的圖片資源會使用到下面的兩個方法:

第一個方法中先使用方法- (NSString *)defaultCachePathForKey:(NSString *)key獲取key值在默認(rèn)情況下保存的路徑盆驹,該路徑是絕對的路徑匹配,不僅匹配到文件名滩愁,還匹配到圖片的后綴類型躯喇,之后利用NSData的+ (instancetype)dataWithContentsOfFile:(NSString *)path方法,獲取本地的圖片二進制數(shù)據(jù)硝枉,如果絕對路徑無法匹配到廉丽,則會使用相對匹配的方法,相對匹配無法找到對應(yīng)數(shù)據(jù)妻味,則會再去遍歷用戶自定義路徑正压,直到找到圖片數(shù)據(jù)為止。

第二個方法的作用則是將獲取到的圖片二進制數(shù)據(jù)進行轉(zhuǎn)化為UIImage對象责球,并對圖片對象進行還原跟解碼操作焦履,保證圖片與網(wǎng)絡(luò)圖片的一致性拓劝。

六.SDImageCache緩存策略之清除

關(guān)于清除操作,SDImageCache提供了三種情況下的清除操作嘉裤,以保證App能夠在特定情況下及時對緩存進行清除郑临,避免出現(xiàn)問題。

a.自動清除機制

SDImageCache在初始化之初就創(chuàng)建了一個名為AutoPurgeCache的對象屑宠,該對象會去監(jiān)聽App拋出內(nèi)存警告的通知UIApplicationDidReceiveMemoryWarningNotification厢洞,一旦App收到該內(nèi)存警告通知,將首先進行清除內(nèi)存中的緩存數(shù)據(jù)侨把,代碼如下:

b.內(nèi)存警告清除以及手動清除本地緩存數(shù)據(jù)

SDImageCache在創(chuàng)建時會添加一個觀察者用以監(jiān)聽UIApplicationDidReceiveMemoryWarningNotification通知犀变,一旦App收到該內(nèi)存警告通知,將會調(diào)用以下方法進行清除緩存數(shù)據(jù)秋柄,使用者也可以根據(jù)場景需要手動清除本地的緩存數(shù)據(jù)获枝,清除操作將會把本地的整個緩存目錄進行清除,代碼如下:

c.后臺狀態(tài)已經(jīng)程序即將關(guān)閉狀態(tài)清除緩存

當(dāng)程序處于后臺時骇笔,SDWebImage庫會開啟后臺任務(wù)機制省店,向系統(tǒng)申請更多的時間去處理線程任務(wù),以保證即使App處于后臺狀態(tài)也能有足夠的時間去清除緩存數(shù)據(jù)笨触,代碼如下:

現(xiàn)在主要看看第二個方法的實現(xiàn)懦傍,該方法通過異步線程的方式在默認(rèn)路徑下對所有文件進行進行過濾操作,這里面有三個枚舉值:

NSURLIsDirectoryKey:允許你判斷遍歷到的URL所指對象是否是目錄.

NSURLContentModificationDateKey:允許你判斷遍歷返回的URL所指項目的最后

修改時間.

NSURLTotalFileAllocatedSizeKey:允許你判斷遍歷返回的URL所指項目的文件的總大小

NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL

includingPropertiesForKeys:resourceKeys

options:NSDirectoryEnumerationSkipsHiddenFiles

errorHandler:NULL];

上面代碼執(zhí)行之后獲取到的值是滿足上面三個枚舉值的目錄下所有文件的URL路徑芦劣,接下來代碼中對fileEnumerator中的URL進一步進行過濾粗俱,剔除目錄路徑的URL,之后將最后修改時間超過一個星期的緩存URL(SDWebImage本地緩存的默認(rèn)的時間為一個星期)放入一個待刪除的數(shù)組urlsToDelete中虚吟,同時保存每個文件的文件大小寸认。如果本地緩存的圖片數(shù)據(jù)的總大小超過設(shè)置的最大值的話,則會根據(jù)文件的最后修改時間進行排序串慰,刪除修改時間靠后的文件偏塞,以保證本地的緩存數(shù)據(jù)不超過最大值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邦鲫,一起剝皮案震驚了整個濱河市灸叼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庆捺,老刑警劉巖古今,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滔以,居然都是意外死亡沧卢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門醉者,熙熙樓的掌柜王于貴愁眉苦臉地迎上來但狭,“玉大人,你說我怎么就攤上這事撬即×⒋牛” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵剥槐,是天一觀的道長唱歧。 經(jīng)常有香客問我,道長粒竖,這世上最難降的妖魔是什么颅崩? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蕊苗,結(jié)果婚禮上沿后,老公的妹妹穿的比我還像新娘。我一直安慰自己朽砰,他們只是感情好尖滚,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞧柔,像睡著了一般漆弄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上造锅,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天撼唾,我揣著相機與錄音,去河邊找鬼哥蔚。 笑死倒谷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肺素。 我是一名探鬼主播恨锚,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倍靡!你這毒婦竟也來了猴伶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤塌西,失蹤者是張志新(化名)和其女友劉穎他挎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捡需,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡办桨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了站辉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呢撞。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡损姜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殊霞,到底是詐尸還是另有隱情摧阅,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布绷蹲,位于F島的核電站棒卷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祝钢。R本人自食惡果不足惜比规,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拦英。 院中可真熱鬧蜒什,春花似錦、人聲如沸龄章。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽做裙。三九已至岗憋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锚贱,已是汗流浹背仔戈。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拧廊,地道東北人监徘。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像吧碾,于是被迫代替她去往敵國和親凰盔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

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