SDImageCacheConfig配置文件
SDImageCacheConfig和SDImageCache是聚合關(guān)系,SDImageCacheConfig為SDImageCache提供一些默認(rèn)的屬性配置:
@property (assign, nonatomic) BOOL shouldDecompressImages;//默認(rèn)是YES,自動解壓圖片,提高性能的同時產(chǎn)生了消耗大量內(nèi)存的副作用。但是如果出現(xiàn)由于消耗大量內(nèi)存導(dǎo)致程序崩潰的問題,可以關(guān)閉這一性能優(yōu)化
@property (assign, nonatomic) BOOL shouldDisableiCloud;//默認(rèn)不在iClound中備份圖片
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;//默認(rèn)在內(nèi)存中緩存圖片
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;//設(shè)置這個枚舉值可以提高磁盤緩存的性能
@property (assign, nonatomic) NSInteger maxCacheAge;//磁盤緩存周期
@property (assign, nonatomic) NSUInteger maxCacheSize;//磁盤緩存最大Size
SDImageCache清理內(nèi)存
AutoPurgeCache
在SDWebimage中含有一個自動清理緩存的類AutoPurgeCache,這個類繼承于NSCache。
在AutoPurgeCache中有一個構(gòu)造方法
- (id)init{
?? self = [super init];
?? if (self) {
???? [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
?? }
return self;
}
這個構(gòu)造方法在初始化時,注冊了一個通知,當(dāng)接受到內(nèi)存警告的通知時,執(zhí)行removeAllObjects
方法,這個方法其實來自于AutoPurgeCache
類的父類NSCache
。
NSCache
NSCache
類是iOS系統(tǒng)中原生的緩存類,這個類和NSDictionary
類十分相似,可以通過鍵值對進行取值/賦值/移除操作
- (nullable ObjectType)objectForKey:(KeyType)key;
- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
- (void)removeObjectForKey:(KeyType)key;
- (void)removeAllObjects;
這里我們需要注意的三個屬性:
@property NSUInteger totalCostLimit; //設(shè)置成本上限,圖片的成本=image.width * image.height
@property NSUInteger countLimit; //設(shè)置緩存數(shù)量上限
@property BOOL evictsObjectsWithDiscardedContent;//自動移除上限,該布爾值標(biāo)識緩存是否自動舍棄那些內(nèi)存已經(jīng)被丟棄的對象,如果設(shè)置為YES,則在對象的內(nèi)存被丟棄時舍棄對象。默認(rèn)值為YES
。
SDImageCache清理磁盤緩存
在SDImageCache中有兩個處理磁盤緩存的方法:
-(void)deleteOldFiles //清理磁盤
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion //清空磁盤
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion
這個方法就是把把磁盤緩存的文件整個刪除,簡單粗暴,這里就不多介紹了,我會著重寫一下-(void)deleteOldFiles
方法。
在初始化SDImageCache時,我們會注冊三個通知:
//清理內(nèi)存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
// 清理磁盤
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deleteOldFiles)
name:UIApplicationWillTerminateNotification
object:nil];
// 后臺清理磁盤
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundDeleteOldFiles)
>name:UIApplicationDidEnterBackgroundNotification
object:nil];
其中第一個通知UIApplicationDidReceiveMemoryWarningNotification
被響應(yīng)時會執(zhí)行clearMemory
方法,這個方法其實也是調(diào)用了NSCache中的removeAllObjects
方法,和AutoPurgeCache
是一樣的原理.
當(dāng)程序退出時,會接受到一個UIApplicationWillTerminateNotification
的通知,此時會執(zhí)行deleteOldFiles ()
方法,這里是清理磁盤 的方法。
在SDWebImage中deleteOldFiles ()
是一個重要函數(shù),接下來好好分析一下SDWebImage清除磁盤緩存的邏輯和思想:
- (void)deleteOldFiles {
[self deleteOldFilesWithCompletionBlock:nil];
}
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
// 磁盤緩存的路徑
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
/* resourceKeys :期望獲取文件的哪些屬性
NSURLIsDirectoryKey, 目錄key
NSURLContentModificationDateKey, url指向的內(nèi)容,最后修改時間的key
NSURLTotalFileAllocatedSizeKey 文件總大小的key
*/
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
// 過期時間,maxCacheAge默認(rèn)是一周,單位是'秒'
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
// 采用字典存儲緩存文件的路徑
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
// 用來 標(biāo)記計算當(dāng)前緩存文件所占空間大小
NSUInteger currentCacheSize = 0;
// 記錄需要刪除的緩存文件路徑
NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
// 遍歷上述根據(jù)條件篩選出來的目錄集合
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
// 根據(jù) 限定的文件信息resourceKeys獲取文件內(nèi)容
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
// 主要判斷如果當(dāng)前文件是目錄,就跳過
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
//拿到最后修改時間
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
// 判斷文件是否過期,過期的話添加到預(yù)先準(zhǔn)備的數(shù)值(存儲需要刪除文件的路徑)中
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
/*1.累計計算未過期文件所在內(nèi)存空間的大小
2.對未過期的文件采用鍵值對的方式存儲起來,為后續(xù)二次清理磁盤緩存做準(zhǔn)備
*/
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
// 刪除過期文件
for (NSURL *fileURL in urlsToDelete) {
[_fileManager removeItemAtURL:fileURL error:nil];
}
// 如果在清除過期文件后,發(fā)現(xiàn)用戶設(shè)置了磁盤緩存最大空間限制 并且當(dāng)前緩存圖片所在總空間大于用戶期望的最大空間限制,對磁盤空間進行二次清理。
if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
// 預(yù)設(shè)一個合理緩存空間大小值,用來和后面清理磁盤緩存后的大小做比較。
const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
// 對緩存文件按照修改時間進行升序排列,時間最早的排在第一個
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// 對緩存文件進行清理,直到緩存所占空間小于設(shè)置的期望值desiredCacheSize
for (NSURL *fileURL in sortedFiles) {
if ([_fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
}
總結(jié)一下清理磁盤的步驟:
- 刪除過期文件
- 計算剩余緩存文件的大小,如果依然超過預(yù)設(shè)置的最大緩存Size,將剩余文件按照最后修改時間進行升序,依次刪除文件,直到剩余緩存文件所占空間小于 1/2 預(yù)設(shè)置的最大緩存空間。
小知識補充:
NS_ENUM和NS_OPTIONS的區(qū)別
- NSInteger(ENUM),OPTIONS(NSUInteger)
- 一個位移枚舉, 一個是整型的
- 如果是options它可以是多個一起使用, enum只能一個單獨進行使用
- 編譯方式不一樣,options C++ NSUInteger
FOUNDATION_STATIC_INLINE
static__inline__ 這表明方法是一個內(nèi)聯(lián)函數(shù),static表示只在當(dāng)前文件可見,避免在多個文件中使用相同的函數(shù)名導(dǎo)致的沖突問題。
在程序中如果我們需要頻繁的調(diào)用一個簡單邏輯的方法,那么將它定義成一個內(nèi)聯(lián)函數(shù)的話,會提升調(diào)用效率。