本章開始將介紹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ù)不超過最大值。