YYDiskCache 磁盘缓存
YYDiskCache是一个线程安全的缓存,用于存储SQLite支持的键值对和文件系统(类似于NSURLCache的磁盘缓存)。
YYDiskCache具有以下功能:
- 它使用LRU(最近最少使用)来删除对象。
- 它可以通过成本,计数和年龄来控制。
3 .它可以配置为在没有可用磁盘空间时自动驱逐对象。 - 它可以自动决定每个对象的存储类型(sqlite / file)。
所以 YYDiskCache 磁盘缓存又分为 文件缓存 和 数据库缓存
1 | /// 根据这个属性进行区分,当大于该值时使用数据库缓存,小于该值使用文件缓存 |
NSMapTable
YYDiskCache 维护一个 NSMapTable(Path : YYDiskCache) 来进行缓存操作
NSMapTable: 类似于 NSDictionary 但是可以指定key-value的引用类型,当value的引用类型为 weak 时,当 value 为 nil 时,自动删除key-value
1 | /// 初始化 |
信号量 dispatch_semaphore
YYDiskCache 使用信号量进行加锁解锁操作。
dispatch_semaphore_t GCD 中信号量,也可以解决资源抢占问题,支持信号通知和信号等待。每当发送一个信号通知,则信号量 +1;每当发送一个等待信号时信号量 -1,;如果信号量为 0 则信号会处于等待状态,直到信号量大于 0(或者超时) 开始执行之后代码。
YYDiskCache
YYDishCache 使用 YYKVStorage 进行文件缓存处理,并自定义了淘汰算法,删除那些最近时间段内不常用的对象。类似于 YYMemoryCache 与 _YYLinkMap 的关系。
1 | @implementation YYDiskCache { |
1 | // 需要通过Path进行初始化 |
边界检查
YYDiskCache 在初始化时也会进行一个递归调用的边界检查任务
1 | - (void)_trimRecursively { |
分别调用 YYKVStorage
的相应方法
注意
- YYDiskCache 默认将数据归档成 Data 然后操作 Data 数据进行缓存
- 如果实现了归档协议则使用归档协议进行存储
YYKVStorage
YYKVStorage是一个基于sqlite和文件系统的键值存储。但作者不建议我们直接使用此类(ps:这个类被封装到了YYDiskCache里,可以通过YYDiskCache间接使用此类),这个类只有一个初始化方法,即initWithPath:type:,初始化后,讲根据path创建一个目录来保存键值数据,初始化后,如果没有得到当前类的实例对象,就表示你不应该对改目录进行读写操作。最后,作者还写了个警告,告诉我们这个类的实例对象并不是线程安全的,你需要确保同一时间只有一条线程访问实例对象,如果你确实需要在多线程中处理大量数据,可以把数据拆分到多个实例对象当中去。
YYKVStorage: YYDiskCache 操作数据的工具类。 YYKVStorage 实现了 文件缓存 和 数据库缓存 功能。可以像操作字典一样使用 key-value 操作数据。
YYKVStorageType 枚举类型,代表使用哪个方式进行存储
1 | typedef NS_ENUM(NSUInteger, YYKVStorageType) { |
YYKVStorageItem: YYKVStorage 操作的数据
1 | @interface YYKVStorageItem : NSObject |
初始化
1 | - (instancetype)initWithPath:(NSString *)path type:(YYKVStorageType)type { |
文件结构
1 | /* |
增删查改
增
- 如果存文件类型且没有文件名,则直接返回NO
- 如果有文件名
- 如果存文件失败,直接返回NO
- 如果存数据库失败(将filename作为value处在数据库里面),则删除文件,并返回NO
- 存文件,存数据库都成功,返回yes
文件名不存在,存数据库类型或者混合存储类型
- 不是存数据库类型,获取文件名,删除文件
- 存数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value filename:(NSString *)filename extendedData:(NSData *)extendedData {
...
// 文件名存在 (>inlineThreshold)
if (filename.length) {
if (![self _fileWriteWithName:filename data:value]) { // 创建文件失败
return NO;
}
if (![self _dbSaveWithKey:key value:value fileName:filename extendedData:extendedData]) { // 存储数据失败
[self _fileDeleteWithName:filename]; // 删除文件
return NO;
}
return YES;
} else {
if (_type != YYKVStorageTypeSQLite) {
NSString *filename = [self _dbGetFilenameWithKey:key];
if (filename) {
[self _fileDeleteWithName:filename];
}
}
return [self _dbSaveWithKey:key value:value fileName:nil extendedData:extendedData];
}
}
删
- 如果是数据库类型,直接删除数据库里面的数据
- 如果是存文件,或者混合类型
- 从数据库获取filename
- 如果文件存在,删除文件
- 删除数据库里面的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25- (BOOL)removeItemForKey:(NSString *)key {
...
switch (_type) {
case YYKVStorageTypeSQLite: {
return [self _dbDeleteItemWithKey:key];
} break;
case YYKVStorageTypeFile:
case YYKVStorageTypeMixed: {
NSString *filename = [self _dbGetFilenameWithKey:key];
if (filename) {
[self _fileDeleteWithName:filename];
}
return [self _dbDeleteItemWithKey:key];
} break;
default: return NO;
}
}
- (BOOL)removeItemForKeys:(NSArray *)keys;
// 删除时间超了的数据
- (BOOL)removeItemsEarlierThanTime:(int)time;
// 当数据总大小大于临界值时删除
- (BOOL)removeItemsToFitSize:(int)maxSize;
// 当数据个数大于临界值时执行删除操作
- (BOOL)removeItemsToFitCount:(int)maxCount;
- (BOOL)removeAllItems;
查
- 文件类型
- 通过key从数据库获取文件名
- 通过文件名获取数据
- 如果没有数据,从数据库删除这条数据
- 数据库类型
- 从数据库根据key获取value data数据
- 混合类型
- 先获取文件名
- 文件名存在,根据文件名获取文件数据,不存在,则删除数据库中数据
- 文件名不存在,直接从数据可获取数据
- 如果获取到值,则更新数据访问时间
- 文件类型
DB操作
_db...
直接操作sqlite数据库,又想了解的自己去看,挺全面的
总结
- YYDiskCache 使用 YYKVStorage 进行文件缓存 和 数据库缓存,提供了一整套接口,让我们使用时就像使用字典一样方便