美文网首页
YYCache源码解析笔记(一)

YYCache源码解析笔记(一)

作者: 梅庆 | 来源:发表于2016-06-27 15:09 被阅读322次

YYKVStorageItem文件

先看头文件

@interface YYKVStorageItem : NSObject
@property (nonatomic, strong) NSString *key;        ///< key
@property (nonatomic, strong) NSData *value;        ///< value
@property (nonatomic, strong) NSString *filename;   ///< filename (nil if inline)
@property (nonatomic, assign) int size;             ///< value's size in bytes
@property (nonatomic, assign) int modTime;          ///< 最后修改时间戳
@property (nonatomic, assign) int accessTime;       ///< 最后访问时间戳
@property (nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
@end

YYKVStorageItem指的是每一个存储的单元,也可以理解为某一个键值对,只是这个存储单元的属性更加完善,还有修改时间戳和访问时间戳,以及补充的数据,这个补充的数据有什么意义呢,我的理解是补充说明这个存储单元。

YYKVStorage文件

typedef NS_ENUM(NSUInteger, YYKVStorageType) {
    
    /// The `value` is stored as a file in file system.
    YYKVStorageTypeFile = 0,
    
    /// The `value` is stored in sqlite with blob type.
    YYKVStorageTypeSQLite = 1,
    
    /// The `value` is stored in file system or sqlite based on your choice.
    YYKVStorageTypeMixed = 2,
};

关于YYKVStorageType,作者分析了YYKVStorageTypeSQLite,YYKVStorageTypeFile这两种存储方式的效率问题,得出了这样的结论:如果你想储存大量的比较小的数据(如联系人缓存),使用YYKVStorageTypeSQLite来获得更好的性能。如果你想存储大文件(如图像缓存),使用YYKVStorageTypeFile来获得更好的性能。当然也可以使用YYKVStorageTypeMixed为每个项目选择存储类型。

YYKVStorage和YYKVStorageItem的关系

YYKVStorage像一个manager用来管理每一个YYKVStorageItem,我们可以通过YYKVStorage来操作单个的YYKVStorageItem。比如保存,移除,获取等等。大家可以对照YYKVStorage文件,里面有非常多的相关操作函数。

保存一个缓存项
- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value filename:(NSString *)filename extendedData:(NSData *)extendedData {
    if (key.length == 0 || value.length == 0) return NO;
    if (_type == YYKVStorageTypeFile && filename.length == 0) {
        return NO;
    }
    
    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];
    }
}

保存一个缓存项的操作步骤:
1.先判断key,value是否为空
2.filename不为空的情况下,先将NSData写入磁盘文件,然后向数据库插入一条磁盘缓存文件的表记录
filename为空的情况下,根据key查询表中记录缓存项的filename字段,删除查询到的filename对
应的缓存文件,更新key对应的缓存表记录的filename字段,清空为nil。

删除多个缓存项
- (BOOL)removeItemForKeys:(NSArray *)keys {
  if (keys.count == 0) return NO;
  switch (_type) {
      case YYKVStorageTypeSQLite: {
          return [self _dbDeleteItemWithKeys:keys];
      } break;
      case YYKVStorageTypeFile:
      case YYKVStorageTypeMixed: {
          NSArray *filenames = [self _dbGetFilenameWithKeys:keys];
          for (NSString *filename in filenames) {
              [self _fileDeleteWithName:filename];
          }
          return [self _dbDeleteItemWithKeys:keys];
      } break;
      default: return NO;
  }
}

删除多个缓存项的操作步骤:
1.先判断数组是否为空
2.判断缓存方式是哪一种
如果是sqlite存储,删除sqlite里对应的缓存项
如果是文件存储,先获取所有文件名,然后删除
如果是混合模式,重复上述步骤

删除所有项
- (BOOL)removeAllItems {
    if (![self _dbClose]) return NO;
    [self _reset];
    if (![self _dbOpen]) return NO;
    if (![self _dbInitialize]) return NO;
    return YES;
}

删除所有目录的操作步骤是:
1.首先关闭数据库,并确认已经关闭
2.格式化数据库,移除db.sqlite,db.sqlite-shm,db.sqlite-wal文件(这三个文件具体是什么职责后面详细介绍),将所有磁盘文件移动到Trash目录下,然后将删除Trash目录下文件的操作放到子线程队列上异步执行
3.初始化数据库

补充:在沙盒下的SQLite数据库包含三个文件:db.sqlite,db.sqlite-shm,db.sqlite-wal。
db.sqlite 存放数据库本身的数据: 表、表记录、index、sequence …
db.sqlite-shm 存放的是Shared-Memory Files,数据库必须开启WAL模式,当多个线程/进程访问同一个数据库时,让每一个数据库连接可以进行内存数据共享,随着 db-wal文件创建而创建,删除而删除.
db.sqlite-wal Write-Ahead Log (WAL) Files WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制。同样数据库必须开启WAL模式,用于当数据库实现实现原子事务,当创建第一个数据库连接时创建 db-wal文件,当最后一个数据库连接关闭时删除 db-wal文件.

查询某个项
- (YYKVStorageItem *)getItemForKey:(NSString *)key {
    if (key.length == 0) return nil;
    YYKVStorageItem *item = [self _dbGetItemWithKey:key excludeInlineData:NO];
    if (item) {
        [self _dbUpdateAccessTimeWithKey:key];
        if (item.filename) {
            item.value = [self _fileReadWithName:item.filename];
            if (!item.value) {
                [self _dbDeleteItemWithKey:key];
                item = nil;
            }
        }
    }
    return item;
}

查询的步骤是:
1.先判断key是否为空
2.通过Key查找到对应的缓存项
3.如果查找成功,更新访问时间,然后通过文件名,读取数据,如果数据为空,则通过key删除对应的文件

查询多个项
- (NSArray *)getItemForKeys:(NSArray *)keys {
    if (keys.count == 0) return nil;
    NSMutableArray *items = [self _dbGetItemWithKeys:keys excludeInlineData:NO];
    if (_type != YYKVStorageTypeSQLite) {
        for (NSInteger i = 0, max = items.count; i < max; i++) {
            YYKVStorageItem *item = items[i];
            if (item.filename) {
                item.value = [self _fileReadWithName:item.filename];
                if (!item.value) {
                    if (item.key) [self _dbDeleteItemWithKey:item.key];
                    [items removeObjectAtIndex:i];
                    i--;
                    max--;
                }
            }
        }
    }
    if (items.count > 0) {
        [self _dbUpdateAccessTimeWithKeys:keys];
    }
    return items.count ? items : nil;
}

查询多个缓存项步骤:
1.先判断需要查找的缓存数组是否为空
2.然后在数据库里拿到所有缓存项
3.遍历出每个缓存项,并确保值存在,不存在的删除掉
4.最后更新所有缓存项的访问时间。

微博账号:梅嘉庆(点击关注)

相关文章

网友评论

      本文标题:YYCache源码解析笔记(一)

      本文链接:https://www.haomeiwen.com/subject/efbzdttx.html