可以来这里下载一下源码注释
5.SDWebImageCache
问题:
①.SDImageCache是怎么存储内存缓存和磁盘缓存的?
②.NSCache 是什么?
③.磁盘缓存的路径是什么?
④.如何清理缓存?何时会自动清理缓存?
⑤.图片解压的作用是什么?
⑥.为什么要用NSMapTable?
枚举
typedef NS_ENUM(NSInteger, SDImageCacheType) {
//没有缓存
SDImageCacheTypeNone,
//磁盘缓存
SDImageCacheTypeDisk,
//内存缓存
SDImageCacheTypeMemory
};
typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
//当图片缓存到内存中时,强制查询磁盘数据
SDImageCacheQueryDataWhenInMemory = 1 << 0,
//默认情况下,我们同步查询内存缓存,异步地查询磁盘缓存。此掩码可以同步查询磁盘缓存。
SDImageCacheQueryDiskSync = 1 << 1
};
.h中的属性
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
//缓存应该持有的对象的最大数量。
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
.m中的属性
@property (strong, nonatomic, nonnull) SDMemoryCache *memCache;
@property (strong, nonatomic, nonnull) NSString *diskCachePath;
@property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths;
@property (strong, nonatomic, nullable) dispatch_queue_t ioQueue;
@property (strong, nonatomic, nonnull) NSFileManager *fileManager;
.h中的方法
//init
+ (nonnull instancetype)sharedImageCache;
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
//缓存路径
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
- (void)addReadOnlyCachePath:(nonnull NSString *)path;
//缓存操作
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
//查询和检索操作
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key;
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
//删除操作
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
//缓存清理
- (void)clearMemory;
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
//缓存配置
- (NSUInteger)getSize;
- (NSUInteger)getDiskCount;
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
//缓存路径
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;
.m中的方法
//init dealloc
+ (nonnull instancetype)sharedImageCache
- (instancetype)init
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
- (void)dealloc
//缓存路径
- (void)addReadOnlyCachePath:(nonnull NSString *)path
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace
//缓存操作
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key
- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key
//查询和检索操作
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock
- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key
- (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data
- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image
- (NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDCacheQueryCompletedBlock)doneBlock
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock
//删除操作
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion
//内存缓存设置
- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost
- (NSUInteger)maxMemoryCost
- (NSUInteger)maxMemoryCountLimit
- (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit
//缓存清理
- (void)clearMemory
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion
- (void)deleteOldFiles
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock
//缓存配置
- (NSUInteger)getSize
- (NSUInteger)getDiskCount
- (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock
SDWebImageManager中直接调用的是 queryCacheOperationForKey这个方法,按照方法调用的顺序来看一下这里的实现:
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock
{
//1.先检查内存缓存, 如果图片在内存中,并且没有强制要求查询磁盘,就返回
//2.创建NSOperation对象
//3.如果NSOperation对象取消了,就return,否则继续
//4.开启异步队列,读取磁盘缓存,
//5.如果有磁盘数据,就解码图像数据
//6.如果解码成功,并且需要缓存到内存中,就添加到内存中
//7.主线程中回调
//8.return operation
}
关于查找磁盘缓存中的图片
- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key
{
//1. defaultCachePathForKey 读取磁盘缓存(沙盒)
//2. 根据路径读取data,如果找到就返回data
//3. 如果没有找到,去掉扩展名再试下
//4. 如果还是没有,就读取bundle中的数据 (addReadOnlyCachePath 获取到的那个)
}
关于存储图片的路径
- (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
// 经过 MD5 处理的文件名
NSString *filename = [self cachedFileNameForKey:key];
// path/<#MD5_filename#>
return [path stringByAppendingPathComponent:filename];
}
- (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key {
//图片存储的路径为 Libray/Cache/<#namespace#>/com.hackemist.SDWebImageCache.<#namespace#>/<#MD5_filename#>
return [self cachePathForKey:key inPath:self.diskCachePath];
}
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSURL *keyURL = [NSURL URLWithString:key];
NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
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], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
return filename;
}
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
// 获取cache目录路径
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return [paths[0] stringByAppendingPathComponent:fullNamespace];
}
关于自动清理磁盘缓存
缓存的配置4.x版本放到了SDImageCacheConfig 里
SDImageCacheConfig
.h中的属性
//解压缩图像下载和缓存可以提高性能,但是会消耗大量内存, 默认是YES ,如果因为消耗内存过大而崩溃,可以置为NO。
//是否解压图片,默认YES
@property (assign, nonatomic) BOOL shouldDecompressImages;
//是否禁用 iCloud 备份,默认是 YES
@property (assign, nonatomic) BOOL shouldDisableiCloud;
//是否缓存到内存中,默认是YES
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
//读取磁盘缓存时的阅读选项。
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;
//在将缓存写入磁盘时写入选项。
@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;
//在缓存中保存图像的最长时间,以秒为单位。(默认一周)
@property (assign, nonatomic) NSInteger maxCacheAge;
//缓存的最大大小,以字节为单位。
@property (assign, nonatomic) NSUInteger maxCacheSize;
.m
#import "SDImageCacheConfig.h"
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 一周,默认超过7天的图片清除
@implementation SDImageCacheConfig
- (instancetype)init {
if (self = [super init]) {
_shouldDecompressImages = YES;
_shouldDisableiCloud = YES;
_shouldCacheImagesInMemory = YES;
_diskCacheReadingOptions = 0;
_diskCacheWritingOptions = NSDataWritingAtomic;
_maxCacheAge = kDefaultCacheMaxCacheAge;
_maxCacheSize = 0;
}
return self;
}
@end
在SDImageCache里,内存缓存是放在 SDMemoryCache中,在SDMemoryCache中,当内存警告时,会删掉内存缓存
SDMemoryCache继承于NSCache,所以在init方法里添加了观察者
SDMemoryCache的 implementation
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
- (instancetype)init {
self = [super init];
if (self) {
// Use a strong-weak maptable storing the secondary cache. Follow the doc that NSCache does not copy keys
// This is useful when the memory warning, the cache was purged. However, the image instance can be retained by other instance such as imageViews and alive.
// At this case, we can sync weak cache back and do not need to load from disk cache
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
self.weakCacheLock = dispatch_semaphore_create(1);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
// Only remove cache, but keep weak cache
[super removeAllObjects];
}
磁盘缓存的清理在SDImageCache中监听
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory
{
……
//当程序将要终止时,异步删掉旧的文件
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deleteOldFiles)
name:UIApplicationWillTerminateNotification
object:nil];
//将要进去后台时,在后台异步删掉旧的文件
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundDeleteOldFiles)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
……
}
关于清理磁盘缓存的操作
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock
{
1.创建异步操作
2.获取缓存文件目录
3. 计算过期日期
4.清理图片
4.1清理过期图片
4.2清理超过最大缓存的图片
}
查询缓存流程图: