AFAutoPurgingImageCache:自動清理的圖片緩存類
一、整體的結(jié)構(gòu):

綠色公有、黃色私有
二、重要屬性:
公有的:
memoryCapacity:緩存最大在內(nèi)存中占據(jù)的大小,默認(rèn)100M
preferredMemoryUsageAfterPurge:進行緩存清理時期望剩余緩存圖片占據(jù)的大小,默認(rèn)60M
memoryUsage:以緩存的圖片占據(jù)的大小私有的:
cachedImages:圖片所在的集合
currentMemoryUsage:記錄當(dāng)前緩存圖片占據(jù)的大小,公有屬性memoryUsage就是讀取它的值
synchronizationQueue:并行隊列,對cachedImages讀、寫任務(wù)由該隊列調(diào)度
三、主要方法
3.1存數(shù)據(jù)的方法
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
// #1:異步任務(wù)向字典集合中添加圖片
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
if (previousCachedImage != nil) {//如果有同一標(biāo)識的AFCachedImage存在就先減去它的大小
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
self.cachedImages[identifier] = cacheImage;
self.currentMemoryUsage += cacheImage.totalBytes;//把新添加的圖片大小計算在內(nèi)
});
// #2:異步任務(wù),若#1操作后緩存總大小大于預(yù)先設(shè)定的大小,這時間順序清楚部分緩存直到preferredMemoryUsageAfterPurge設(shè)置的大小
dispatch_barrier_async(self.synchronizationQueue, ^{
if (self.currentMemoryUsage > self.memoryCapacity) {
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
self.currentMemoryUsage -= bytesPurged;
}
});
}
這個方法中兩次用到dispatch_barrier_async()向并行隊列中添加異步任務(wù),這兩個任務(wù)會等待隊列中的其他任務(wù)執(zhí)行完畢后再開始執(zhí)行,就像一道“墻”一樣保證了同一時刻只有一個線程執(zhí)行寫操作

3.2清除數(shù)據(jù)數(shù)據(jù)的方法
- (BOOL)removeImageWithIdentifier:(NSString *)identifier {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
if (cachedImage != nil) {
[self.cachedImages removeObjectForKey:identifier];
self.currentMemoryUsage -= cachedImage.totalBytes;
removed = YES;
}
});
return removed;
}
- (BOOL)removeAllImages {...}
清除數(shù)據(jù)也是一種寫操作,所有要用barrier;又因為要以返回值的形式向外傳遞處理結(jié)果,所以要用sync同步操作。
3.3獲取數(shù)據(jù)的方法
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
__block UIImage *image = nil;
dispatch_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
image = [cachedImage accessImage];
});
return image;
}
同步獲取數(shù)據(jù)是一種讀操作,多個任務(wù)可以同時進行。如下圖:

異步的寫法:
- (void)imageWithIdentifier:(NSString *)identifier completion:(void (^)(UIImage *))handler {
__block UIImage *image = nil;
dispatch_async(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
image = [cachedImage accessImage];
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(image);
}
});
});
}
總結(jié)
這個類相對簡單而且可以單獨使用,從中可以學(xué)習(xí)到處理非線程安全的對象(這里是NSMutableDictionary)時的一些方法。