## SDWebImage学习-SDImageCache


####问题

缓存怎么设计的（整体结构、工作流）

涉及哪些类，都是干什么的

有哪些缓存规则和配置选项，怎么配置

内存缓存和磁盘缓存分别是怎么工作的？

缓存读取同步还是异步，能否取消缓存读取

一、二级内存缓存怎么协作

缓存的图片是否是已解码的？

从缓存读取到的图片是解码后的吗？

缓存文件的命名

缓存读取如何保证线程安全

内存警告怎么处理

磁盘缓存目录

缓存什么时候清除？

缓存清除策略，按什么清除

如何自定义缓存

....


####一、SDImageCacheDefine
主要是一些基础定义

缓存类型枚举  SDImageCacheType

一系列缓存操作Block ,SDImageCacheCheckCompletionBlock等

缓存查询过程中的解码方法SDImageCacheDecodeImageData

定义了核心协议-SDImageCache，自定义缓存时只要实现该协议的方法，可以通过SDImageCachesManager来注册管理多个缓存

####二、SDImageCacheConfig
1、配置信息和选项
SDImageCacheConfigExpireType，用来计算图片的过期时间的选项

2、列举一些属性
```@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;```

关于NSDataReadingOptions有几个选项
NSDataReadingUncached : 不要缓存，如果该文件只会读取一次，这个设置可以提高性能；
NSDataReadingMappedIfSafe : 在保证安全的前提下使用mmap；
NSDataReadingMappedAlways : 使用mmap；
可以传入NSDataReadingMappedIfSafe，这样系统就不会把整个文件读进内存，而是用进程的内存虚拟地址空间去映射实际的文件中，mmap不会为文件分配物理内存，而是相当于将内存地址指向文件的磁盘地址。一定场景下可以用来解决内存暴涨问题，例如加载一个视频文件，如果一下子加载完整的视频文件，内存可能暴涨,采用mmap来优化，建立文件映射，就可以有效减少内存占用。

```@property (assign, nonatomic) NSDataWritingOptions     diskCacheWritingOptions;```
 将缓存写入磁盘时的写入选项，默认为`NSDataWritingAtomic`，但是可以设置为“NSDataWritingWithoutOverwriting”以防止覆盖现有文件
 
 支持自定义缓存类
```@property (assign, nonatomic, nonnull) Class memoryCacheClass;
   @property (assign ,nonatomic, nonnull) Class diskCacheClass;
```

####三、SDImageCache类

1、基本组成
缓存的核心类，只要管理内存缓存和磁盘缓存
主要属性
```
// 一些缓存的选项
@property (nonatomic, copy, nonnull, readonly) SDImageCacheConfig *config;

// 内存缓存
@property (nonatomic, strong, readonly, nonnull) id<SDMemoryCache> memoryCache;

//配置对象
@property (nonatomic, copy, nonnull, readonly) SDImageCacheConfig *config;

 //磁盘缓存
@property (nonatomic, strong, readonly, nonnull) id<SDDiskCache> diskCache;

//缓存路径
@property (nonatomic, copy, nonnull, readonly) NSString *diskCachePath;

//附加磁盘缓存路径,app预加载缓存可以用这个
@property (nonatomic, copy, nullable) SDImageCacheAdditionalCachePathBlock additionalCachePathBlock;
```

初始化方法
可以指定命名空间和沙盒目录中，命名空间也就是保存图像的文件夹名字
-initWithNamespace: diskCacheDirectory:    

一系列存储、查询、删除和清理缓存等操作接口
缓存清理接口
获取缓存相关信息接口

SDImageCache扩展，遵循了SDImageCache协议


2、具体实现

根据config的属性创建memoryCache和diskCache，同时也把config同步过去

migrateDiskCacheDirectory操作，把旧文件夹的缓存移动到新文件夹路径下

设置监听
UIApplicationWillTerminateNotification
UIApplicationDidEnterBackgroundNotification
处理删除磁盘中过期的数据
核心代码
[self.diskCache removeExpiredData];
 
1）读取磁盘缓存路径
2）读取缓存超时类型，根据config.diskCacheExpireType，
将 SDImageCacheConfigExpireType(图片的过期时间类型) 和 NSURLResourceKey做一个映射。也就是我们自己可以选择超时的判断标准，比如可以创建时间、修改时间或者访问时间是否超时来决定是否删除。
3）调用文件夹遍历函数enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:，过滤掉隐藏文件、resourceKeys指定便随URL预取的资源属性，这里指定了三个属性：是否是目录、过期类型、为文件分配的空间

```

   NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, cacheContentDateKey, NSURLTotalFileAllocatedSizeKey];

 NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
                                               includingPropertiesForKeys:resourceKeys
                                                                  options:NSDirectoryEnumerationSkipsHiddenFiles
                                                             errorHandler:NULL];
   ```
 NSURLIsDirectoryKey 允许判断遍历到的URL所指对象是否是目录
 NSDirectoryEnumerationSkipsHiddenFiles 跳过隐藏文件
 
  4）删除过期的图片
  
 5）在缓存目录下遍历图片时，除了把过期的缓存图片移除，还需要把未过期的图片添加到cacheFiles，用来进行后续的清理逻辑（存储文件属性resourceKeys，通过size判断）
 6)根据配置的SDImageCacheConfig.maxCacheSize判断是否超过大小，如果超过，需要对剩下的图片进行二次清理，直到缓存目录大小 小于等于 config.maxCacheSize/2
 
 缓存清除时机
收到内存就警告: 清理内存缓存
App将要被杀死: 清理磁盘缓存
App进入后台: 在后台清理磁盘缓存


Caches Manager




