SDWebImage解析之SDImageCache
SDWebImage是我们最常见的一个第三方库图片缓存库,它提供了UIImageView的一个分类,以支持从远程服务器下载并缓存图片的功能。它具有以下功能:
- 支持网络图片的异步加载与缓存管理 并具有自动缓存过期处理功能
- 支持GIF图片
- 后台图片解压缩处理
- 确保同一个URL的图片不被下载多次
- 确保虚假的URL不会被反复加载
- 确保下载及缓存时,主线程不被阻塞
- 使用GCD和ARC
- 支持ARM64
本文将对SDImageCache进行解析,如有分析不到位之处,还请广大读者指出!
SDImageCache维护一个内存缓存和一个可选的磁盘缓存
SDImageCache的实例
+ (SDImageCache *)sharedImageCache
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
初始化方法
-
默认初始化方法创建一个default名字的缓存文件夹
- (id)init
{
return [self initWithNamespace:@"default"];
} -
用此方法自定义缓存文件夹名字
- (id)initWithNamespace:(NSString *)ns
{
if ((self = [super init]))
{
//命名空间
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];// 创建IO队列 _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); // Init default values 初始化缓存时间(默认最大 一周) _maxCacheAge = kDefaultCacheMaxCacheAge; // Init the memory cache 初始化cache _memCache = [[NSCache alloc] init]; _memCache.name = fullNamespace; // Init the disk cache 设置本地缓存路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); _diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace]; dispatch_sync(_ioQueue, ^ { _fileManager = NSFileManager.new; }); //添加观察者对象 处理内存警告时 清空缓存 #if TARGET_OS_IPHONE // Subscribe to app events 清空内存缓存 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; // 清空disk缓存 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cleanDisk) name:UIApplicationWillTerminateNotification object:nil]; //后台清空disk缓存 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundCleanDisk) name:UIApplicationDidEnterBackgroundNotification object:nil]; #endif } return self; }
-
清空内存缓存
- (void)clearMemory
{
[self.memCache removeAllObjects];
} -
清空磁盘缓存
- (void)clearDisk
{
dispatch_async(self.ioQueue, ^
{
[[NSFileManager defaultManager] removeItemAtPath:self.diskCachePath error:nil];
[[NSFileManager defaultManager] createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
});
} -
清空所有过期的磁盘缓存
- (void)cleanDisk
{
dispatch_async(self.ioQueue, ^
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray *resourceKeys = @[ NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ];// This enumerator prefetches useful properties for our cache files. NSDirectoryEnumerator *fileEnumerator = [fileManager enumeratorAtURL:diskCacheURL includingPropertiesForKeys:resourceKeys options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:NULL]; NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary]; unsigned long long currentCacheSize = 0; // Enumerate all of the files in the cache directory. This loop has two purposes: // // 1. Removing files that are older than the expiration date. // 2. Storing file attributes for the size-based cleanup pass. for (NSURL *fileURL in fileEnumerator) { NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL]; // Skip directories. if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { continue; } // Remove files that are older than the expiration date; NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { [fileManager removeItemAtURL:fileURL error:nil]; continue; } // Store a reference to this file and account for its total size. NSNumber *totalAllocatedSize = reso urceValues[NSURLTotalFileAllocatedSizeKey]; currentCacheSize += [totalAllocatedSize unsignedLongLongValue]; [cacheFiles setObject:resourceValues forKey:fileURL]; } // If our remaining disk cache exceeds a configured maximum size, perform a second // size-based cleanup pass. We delete the oldest files first. if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) { // Target half of our maximum cache size for this cleanup pass. const unsigned long long desiredCacheSize = self.maxCacheSize / 2; // Sort the remaining cache files by their last modification time (oldest first). NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]]; }]; // Delete files until we fall below our desired cache size. for (NSURL *fileURL in sortedFiles) { if ([fileManager removeItemAtURL:fileURL error:nil]) { NSDictionary *resourceValues = cacheFiles[fileURL]; NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; currentCacheSize -= [totalAllocatedSize unsignedLongLongValue]; if (currentCacheSize < desiredCacheSize) { break; } } } } }); }
-
后台运行时清空磁盘缓存
- (void)backgroundCleanDisk
{
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^
{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];// Start the long-running task and return immediately. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { // Do the work associated with the task, preferably in chunks. [self cleanDisk]; [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }); }
-
磁盘高速缓存使用的大小
- (unsigned long long)getSize; -
磁盘缓存的图片数量
- (int)getDiskCount; -
异步计算磁盘高速缓存的大小。
- (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, unsigned long long totalSize))completionBlock; -
检查图像是否存在于缓存了
- (BOOL)diskImageExistsWithKey:(NSString *)key; -
通过key 将image 缓存到内存中
- (void)storeImage:(UIImage *)image forKey:(NSString *)key -
通过key 将image 缓存 toDisk==TRUE 标示缓存到磁盘中 toDisk==FALSE缓存到内存中
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk; -
通过key 将image 缓存 image图片 如果data不为nil 则直接缓存data数据 如果data为nil 则将image转为NSData再存储 toDisk==TRUE 标示缓存到磁盘中 toDisk==FALSE缓存到内存中
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk; -
通过key异步查询磁盘高速缓存
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock; -
通过key异步查询内存缓存
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key; -
通过key从磁盘和内存中移除缓存
- (void)removeImageForKey:(NSString *)key; -
通过key从磁盘或内存中移除缓存 fromDisk==TRUE 从磁盘中移除反之从内存中移除
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;