1. 前言
前面的代碼并沒有特意去講SDWebImage的緩存機制,主要是想單獨開一章節(jié)專門講解緩存。之前我們也遇到一些緩存的屬性和方法,比如storeImage、queryDiskCacheForKey、memCache等等。
SDWebImage的緩存分為兩個部分,一個內(nèi)存緩存,使用NSCache實現(xiàn),另一個就是硬盤緩存(disk),使用NSFileManager實現(xiàn)。
不過這么多函數(shù),我們先從哪看起呢?就從給我印象最深的queryDiskCacheForKey看起。主要是因為這個函數(shù)返回的是一個NSOperation。和SDWebImageManager關(guān)系緊密,尤其是和SDWebImageCombinedOperation的cacheOperation,直接就是作為其返回值。
2. queryDiskCacheForKey
之前簡單的介紹了一下queryDiskCacheForKey函數(shù)實現(xiàn)。具體的細(xì)節(jié)并沒有介紹。尤其是對queryDiskCacheForKey中的done block中有關(guān)cache的部分沒有細(xì)說。這里doneBlock先不討論,先討論queryDiskCacheForKey中的cache部分。
最先看到的關(guān)于cache的部分:
// 首先根據(jù)key(一般指的是圖片的url)去內(nèi)存緩存獲取image
UIImage *image = [self imageFromMemoryCacheForKey:key];
這個imageFromMemoryCacheForKey的具體實現(xiàn):
// 簡單的封裝了NSCache的objectForKey方法
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey:key];
}
這里,很自然就會想到,objectForKey的對應(yīng)方法是setObject:forKey:。所以我們搜一下看看SDWebImage在哪里使用了setObject:forKey:
總共有三處地方:
queryDiskCacheForKey:done:
imageFromDiskCacheForKey:key
storeImage:recalculateFromImage:imageData:forKey:toDisk
2.1 queryDiskCacheForKey:done:
// 獲取到disk上緩存的image
UIImage *diskImage = [self diskImageForKey:key];
// 如果diskImage存在,并且需要使用memory cache
// 就將diskImage緩存到memory cache中
if (diskImage && self.shouldCacheImagesInMemory) {
// cost 被用來計算緩存中所有對象的代價。當(dāng)內(nèi)存受限或者所有緩存對象的總代價超過了最大允許的值時,緩存會移除其中的一些對象。
// 通常,精確的 cost 應(yīng)該是對象占用的字節(jié)數(shù)。
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
這里只有SDCacheCostForImage函數(shù)需要細(xì)看一下,該函數(shù)本質(zhì)就是計算diskImage所要占用的字節(jié)數(shù):
// C語言函數(shù)
// FOUNDATION_STATIC_INLINE表示static __inline__,屬于runtime范疇
FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
// 這里我覺得這樣寫不是很好,如果這樣寫就更直觀了
// return (height * scale) * (width * scale)
return image.size.height * image.size.width * image.scale * image.scale;
}
2.2 imageFromDiskCacheForKey:key
用途和上面一樣,就是從disk中獲取的image,還需更新到內(nèi)存緩存中。
2.3 storeImage:recalculateFromImage:imageData:forKey:toDisk
其實也是和上面一樣,都是為了更新到內(nèi)存緩存中:
// 如果可以使用內(nèi)存緩存
if (self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
}
這里比較關(guān)鍵的其實是storeImage這個函數(shù)的使用場景:
- 在SDWebImageManager中的downloadImageWithURL中,成功下載到圖片downloadedImage后(或者進行了transform)使用該函數(shù)對image進行緩存
我們來具體分析下這個函數(shù)。函數(shù)中的前兩個if語句比較好理解,也解釋過了。主要集中在if(toDisk)這個語句中,而toDisk為YES表示應(yīng)該是要往disk memory中存儲。
整個語句塊是放在一個ioQueue的dispatch_queue_t中的:
dispatch_async(self.ioQueue, ^{
// ......
});
這個ioQueue,我們從字面上理解,就是一個磁盤io的dispatch_queue_t。說簡單點,就是每個下載來的圖片,需要進行磁盤io的過程都放在ioQueue中執(zhí)行。
剩下的部分主要做了兩件事:
- 1.根據(jù)imageData和image生成待存儲的data
- 2.利用NSFileManager將待存儲的data存儲起來
// 構(gòu)建一個data,用來存儲到disk中,默認(rèn)值為imageData
NSData *data = imageData;
// 如果image存在,但是需要重新計算(recalculate)或者data為空
// 那就要根據(jù)image重新生成新的data
// 不過要是連image也為空的話,那就別存了
if (image && (recalculate || !data)) {
#if TARGET_OS_IPHONE
// 我們需要判斷image是PNG還是JPEG
// PNG的圖片很容易檢測出來,因為它們有一個特定的標(biāo)示 (http://www.w3.org/TR/PNG-Structure.html)
// PNG圖片的前8個字節(jié)不許符合下面這些值(十進制表示)
// 137 80 78 71 13 10 26 10
// 如果imageData為空l (舉個例子,比如image在下載后需要transform,那么就imageData就會為空)
// 并且image有一個alpha通道, 我們將該image看做PNG以避免透明度(alpha)的丟失(因為JPEG沒有透明色)
int alphaInfo = CGImageGetAlphaInfo(image.CGImage); // 獲取image中的透明信息
// 該image中確實有透明信息,就認(rèn)為image為PNG
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL imageIsPng = hasAlpha;
// 但是如果我們已經(jīng)有了imageData,我們就可以直接根據(jù)data中前幾個字節(jié)判斷是不是PNG
if ([imageData length] >= [kPNGSignatureData length]) {
// ImageDataHasPNGPreffix就是為了判斷imageData前8個字節(jié)是不是符合PNG標(biāo)志
imageIsPng = ImageDataHasPNGPreffix(imageData);
}
// 如果image是PNG格式,就是用UIImagePNGRepresentation將其轉(zhuǎn)化為NSData,否則按照J(rèn)PEG格式轉(zhuǎn)化,并且壓縮質(zhì)量為1,即無壓縮
if (imageIsPng) {
data = UIImagePNGRepresentation(image);
}
else {
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
}
#else
// 當(dāng)然,如果不是在iPhone平臺上,就使用下面這個方法。不過不在我們研究范圍之內(nèi)
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
}
// 獲取到需要存儲的data后,下面就要用fileManager進行存儲了
if (data) {
// 首先判斷disk cache的文件路徑是否存在,不存在的話就創(chuàng)建一個
// disk cache的文件路徑是存儲在_diskCachePath中的
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// 根據(jù)image的key(一般情況下理解為image的url)組合成最終的文件路徑
// 上面那個生成的文件路徑只是一個文件目錄,就跟/cache/images/img1.png和cache/images/的區(qū)別一樣
// defaultCachePathForKey后面會詳解
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// 這個url可不是網(wǎng)絡(luò)端的url,而是file在系統(tǒng)路徑下的url
// 比如/foo/bar/baz --------> file:///foo/bar/baz
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
// 根據(jù)存儲的路徑(cachePathForKey)和存儲的數(shù)據(jù)(data)將其存放到iOS的文件系統(tǒng)
[_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil];
// 如果不使用iCloud進行備份,就使用NSURLIsExcludedFromBackupKey
if (self.shouldDisableiCloud) {
[fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
2.3.1 defaultCachePathForKey
// 簡單封裝了cachePathForKey:inPath
- (NSString *)defaultCachePathForKey:(NSString *)key {
return [self cachePathForKey:key inPath:self.diskCachePath];
}
// cachePathForKey:inPath
- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
// 根據(jù)傳入的key創(chuàng)建最終要存儲時的文件名
NSString *filename = [self cachedFileNameForKey:key];
// 將存儲的文件路徑和文件名綁定在一起,作為最終的存儲路徑
return [path stringByAppendingPathComponent:filename];
}
// cachedFileNameForKey:
- (NSString *)cachedFileNameForKey:(NSString *)key {
const char *str = [key UTF8String];
if (str == NULL) {
str = "";
}
// 使用了MD5進行加密處理
// 開辟一個16字節(jié)(128位:md5加密出來就是128bit)的空間
unsigned char r[CC_MD5_DIGEST_LENGTH];
// 官方封裝好的加密方法
// 把str字符串轉(zhuǎn)換成了32位的16進制數(shù)列(這個過程不可逆轉(zhuǎn)) 存儲到了r這個空間中
CC_MD5(str, (CC_LONG)strlen(str), r);
// 最終生成的文件名就是 "md5碼"+".文件類型"
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];
return filename;
}
2.4 小結(jié)
上面的幾個分析基本上已經(jīng)把內(nèi)存緩存的存儲和磁盤緩存的存儲講了一下。
- 內(nèi)存緩存的存儲主要就是使用NSCache自帶的setObject:forKey:及其衍生方法
- 硬盤緩存的存儲主要是使用NSFileManager進行存儲
已經(jīng)講完存儲了,那么就不得不提及clear緩存。
3. clear緩存
我們簡單看一下clear的方式,發(fā)現(xiàn)以下幾個函數(shù)需要注意:
removeImageForKeyfromDisk:withCompletion: // 異步地將image從緩存(內(nèi)存緩存以及可選的磁盤緩存)中移除
clearMemory // 清楚內(nèi)存緩存上的所有image
clearDisk // 清除磁盤緩存上的所有image
cleanDisk // 清除磁盤緩存上過期的image
3.1 removeImageForKeyfromDisk:withCompletion:
這個函數(shù)其實是removeImageForKey:等一系列函數(shù)的基礎(chǔ),類似sd_setImageWithURL:placeholderImage:options:progress:completed:函數(shù)。
該函數(shù)主要是根據(jù)key來刪除對應(yīng)緩存image:
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion {
if (key == nil) {
return;
}
// shouldCacheImagesInMemory為YES表示該圖片會緩存到了內(nèi)存
// 既然緩存到了內(nèi)存,就要先將內(nèi)存緩存中的image移除
// 使用的是NSCache的removeObjectForKey:
if (self.shouldCacheImagesInMemory) {
[self.memCache removeObjectForKey:key];
}
// 如果要刪除磁盤緩存中的image
if (fromDisk) {
// 有關(guān)io的部分,都要放在ioQueue中
dispatch_async(self.ioQueue, ^{
// 磁盤緩存移除使用的是NSFileManager的removeItemAtPath:error
[_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];
// 如果用戶實現(xiàn)了completion了,就在主線程調(diào)用completion()
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
} else if (completion){ // 如果用戶實現(xiàn)了completion了,就在主線程調(diào)用completion()
completion();
}
}
3.2 clearMemory
簡單地調(diào)用NSCache的removeAllObjects。
3.3 clearDisk
封裝了clearDiskOnCompletion:函數(shù):
- (void)clearDisk {
[self clearDiskOnCompletion:nil];
}
clearDiskOnCompletion:
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion
{
dispatch_async(self.ioQueue, ^{
// 先將存儲在diskCachePath中緩存全部移除,然后新建一個空的diskCachePath
[_fileManager removeItemAtPath:self.diskCachePath error:nil];
[_fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
// 如果實現(xiàn)了completion,就在主線程中調(diào)用
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
}
3.4 cleanDisk
簡單封裝了cleanDiskWithCompletionBlock:
- (void)cleanDisk {
[self cleanDiskWithCompletionBlock:nil];
}
cleanDiskWithCompletionBlock:
// 實現(xiàn)了一個簡單的緩存清除策略:清除修改時間最早的file
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
// 這兩個變量主要是為了下面生成NSDirectoryEnumerator準(zhǔn)備的
// 一個是記錄遍歷的文件目錄,一個是記錄遍歷需要預(yù)先獲取文件的哪些屬性
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// 遞歸地遍歷diskCachePath這個文件夾中的所有目錄,此處不是直接使用diskCachePath,而是使用其生成的NSURL
// 此處使用includingPropertiesForKeys:resourceKeys,這樣每個file的resourceKeys對應(yīng)的屬性也會在遍歷時預(yù)先獲取到
// NSDirectoryEnumerationSkipsHiddenFiles表示不遍歷隱藏文件
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
// 獲取文件的過期時間,SDWebImage中默認(rèn)是一個星期
// 不過這里雖然稱*expirationDate為過期時間,但是實質(zhì)上并不是這樣。
// 其實是這樣的,比如在2015/12/12/00:00:00最后一次修改文件,對應(yīng)的過期時間應(yīng)該是
// 2015/12/19/00:00:00,不過現(xiàn)在時間是2015/12/27/00:00:00,我先將當(dāng)前時間減去1個星期,得到
// 2015/12/20/00:00:00,這個時間才是我們函數(shù)中的expirationDate。
// 用這個expirationDate和最后一次修改時間modificationDate比較看誰更晚就行。
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
// 用來存儲對應(yīng)文件的一些屬性,比如文件所需磁盤空間
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
// 記錄當(dāng)前已經(jīng)使用的磁盤緩存大小
NSUInteger currentCacheSize = 0;
// 在緩存的目錄開始遍歷文件. 此次遍歷有兩個目的:
//
// 1. 移除過期的文件
// 2. 同時存儲每個文件的屬性(比如該file是否是文件夾、該file所需磁盤大小,修改時間)
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
// 當(dāng)前掃描的是目錄,就跳過
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// 移除過期文件
// 這里判斷過期的方式:對比文件的最后一次修改日期和expirationDate誰更晚,如果expirationDate更晚,就認(rèn)為該文件已經(jīng)過期,具體解釋見上面
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// 計算當(dāng)前已經(jīng)使用的cache大小,
// 并將對應(yīng)file的屬性存到cacheFiles中
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
}
for (NSURL *fileURL in urlsToDelete) {
// 根據(jù)需要移除文件的url來移除對應(yīng)file
[_fileManager removeItemAtURL:fileURL error:nil];
}
// 如果我們當(dāng)前cache的大小已經(jīng)超過了允許配置的緩存大小,那就刪除已經(jīng)緩存的文件。
// 刪除策略就是,首先刪除修改時間更早的緩存文件
if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
// 直接將當(dāng)前cache大小降到允許最大的cache大小的一般
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
// 根據(jù)文件修改時間來給所有緩存文件排序,按照修改時間越早越在前的規(guī)則排序
NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// 每次刪除file后,就計算此時的cache的大小
// 如果此時的cache大小已經(jīng)降到期望的大小了,就停止刪除文件了
for (NSURL *fileURL in sortedFiles) {
if ([_fileManager removeItemAtURL:fileURL error:nil]) {
// 獲取該文件對應(yīng)的屬性
NSDictionary *resourceValues = cacheFiles[fileURL];
// 根據(jù)resourceValues獲取該文件所需磁盤空間大小
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
// 計算當(dāng)前cache大小
currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
// 如果有completionBlock,就在主線程中調(diào)用
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
}
4. init方法
講完了一些具體的緩存方法。反過來,我們研究下SDImageCache的初始化。因為之前的很多方法中的參數(shù)都是已經(jīng)在init中設(shè)置好了。另外一個原因是SDImageCache使用了單例模式。所以相對來說,init方法還是很重要的。
我們先從單例模式看起,正好學(xué)習(xí)下單例模式的正確寫法:
// SDImageCache使用的是單例模式
+ (SDImageCache *)sharedImageCache {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
// new = alloc + init
instance = [self new];
});
return instance;
}
4.1 initWithNamespace:
接著我們來看看init方法,原來封裝了initWithNamespace:方法,并且namespace的名稱為@"default"。
- (id)initWithNamespace:(NSString *)ns {
// iOS使用的是沙盒機制,此處makeDiskCachePath就是獲取Cache目錄,并在Cache目錄下創(chuàng)建default目錄
// 比如我的mac上就顯示/Users/poloby/Library/Developer/CoreSimulator/Devices/4404872F-4DDD-4AEA-AAD3-71BA1931D4C1/data/Containers/Data/Application/9C7E5D14-FBF0-41F1-A533-E8ACC59FCBAC/Library/Caches/default
// 后面詳解
NSString *path = [self makeDiskCachePath:ns];
// 最終的初始化,后面詳解
return [self initWithNamespace:ns diskCacheDirectory:path];
}
4.1.1 makeDiskCachePath:
- (NSString *)makeDiskCachePath:(NSString*)fullNamespace{
// 獲取當(dāng)前用戶應(yīng)用下的Caches目錄
// 返回了一個包含用戶Caches目錄作為第一元素的數(shù)組,所以底下用的是paths[0]
// 即/Users/poloby/Library/Developer/CoreSimulator/Devices/4404872F-4DDD-4AEA-AAD3-71BA1931D4C1/data/Containers/Data/Application/9C7E5D14-FBF0-41F1-A533-E8ACC59FCBAC/Library/Caches/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
// 在Caches目錄下構(gòu)建一個fullNamespace目錄,此處默認(rèn)是default目錄
return [paths[0] stringByAppendingPathComponent:fullNamespace];
}
4.1.2 initWithNamespace:diskCacheDirectory:
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
if ((self = [super init])) {
// 再給Caches/default/后面加上fullNamspace
// 最終可能獲得的diskCachePath可能為
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// 初始化kPNGSignatureData為PNG前8字節(jié)的標(biāo)志:{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
// 用于ImageDataHasPNGPreffix這個C函數(shù)中,判斷該data是不是PNG格式
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
// 創(chuàng)建名為com.hackemist.SDWebImageCache的IO的串行隊列
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
// cache存儲的最長時間為60 * 60 * 24 * 7,即一個星期
_maxCacheAge = kDefaultCacheMaxCacheAge;
// 注意此處不是直接使用[[NSCache alloc] init]進行初始化的,而是使用了一個AutoPurgeCache
// AutoPurgeCache和NSCache不同之處在于,如果AutoPurgeCache收到一個內(nèi)存警告,就會自動釋放內(nèi)存,調(diào)用NSCache的removeAllObjects
_memCache = [[AutoPurgeCache alloc] init];
_memCache.name = fullNamespace;
// 初始化disk cache,一般情況下directory,除非你把Caches刪除了
if (directory != nil) {
// 最終結(jié)果是/Users/poloby/Library/Developer/CoreSimulator/Devices/4404872F-4DDD-4AEA-AAD3-71BA1931D4C1/data/Containers/Data/Application/9C7E5D14-FBF0-41F1-A533-E8ACC59FCBAC/Library/Caches/default/com.hackemist.SDWebImageCache.default
_diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
} else {
// 如果沒有找到Caches目錄,或者新建default目錄失敗。就重新使用makeCachePath新建一個緩存目錄
NSString *path = [self makeDiskCachePath:ns];
_diskCachePath = path;
}
// 默認(rèn)需要解壓縮圖片
_shouldDecompressImages = YES;
// 新建一個NSFileManager也是放在ioQueue中的
dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
});
#if TARGET_OS_IPHONE
// 訂閱了app可能發(fā)生的時間
// 出現(xiàn)內(nèi)存警告(UIApplicationDidReceiveMemoryWarningNotification),調(diào)用clearMemory
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
// 程序終止(UIApplicationWillTerminateNotification),調(diào)用cleanDisk
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];
// 程序進入后臺運行(UIApplicationDidEnterBackgroundNotification),調(diào)用backgroundCleanDisk
// backgroundCleanDisk就不贅述了,其實現(xiàn)了在后臺注冊了cleanDiskWithCompletionBlock函數(shù)來處理后臺的磁盤緩存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
}
return self;
}
5. 總結(jié)
SDImageCache部分有些地方處理還是很簡單的,比如清除緩存策略。如果有大牛實現(xiàn)LRU策略就更好了。
SDWebImage源碼解析到此為止,當(dāng)然還有一些模塊沒有解析,比如MKAnnotationView+WebCache.h、UIButton+WebCache.h、UIImageView+HighlightedWebCache.h以及一些模塊的某些函數(shù)也沒細(xì)講。不過相信大家舉一反三的能力還是很強的。
6. 參考文章
本文轉(zhuǎn)載polobymulberry-博客園