SDWebImage 4.x版本源码分析(四)SDWebImageCache

可以来这里下载一下源码注释

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 *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 *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清理超过最大缓存的图片
}

查询缓存流程图:

SDWebImage 4.x版本源码分析(四)SDWebImageCache_第1张图片
image.png

你可能感兴趣的:(SDWebImage 4.x版本源码分析(四)SDWebImageCache)