iOS缓存管理,YYCache的基本使用

1,819 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

YYCache

日常开发中,用到文件缓存必不可少,YYCache一款曾今风靡iOS圈的框架,简单易用且性能高效,磁盘缓存或内存缓存均支持,虽然是一款很老的框架,但依然是OC项目的首选。

实际项目使用时,我们可以将其二次封装成一个工具类使用,以下是项目中简单缓存音乐的例子。

@interface CCacheTool ()
@property (nonatomic, strong, readonly) YYCache *musicCache;  ///< 音乐缓存管理

@end

@implementation CCacheTool

+ (instancetype)share {
    static CCacheTool *tool = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        tool = [[CCacheTool alloc] init];
        [tool.musicCache.memoryCache setCountLimit:0]; //内存最大缓存数据个数,因为是音乐大文件,不需要内存缓存
        [tool.musicCache.diskCache setCountLimit:20]; //磁盘最大缓存数据个数
        [tool.musicCache.diskCache setCostLimit:1000000*100]; //磁盘最大缓存数据个数
        [tool.musicCache.diskCache setAutoTrimInterval:60]; //设置磁盘lru动态清理频率

    });

    return tool;

}

- (instancetype)init {
    self = [super init];
    if (self) {
        _musicCache = [YYCache cacheWithName:@"MusicCache"];
        _musicCache.diskCache.customFileNameBlock = ^NSString * _Nonnull(NSString * _Nonnull key) {
            return [NSString stringWithFormat:@"%@.mp3",key];
        };

    return self;

}

  • 可以使用customFileNameBlock自定义存储key,默认将以md5命名

缓存大小查询

  • 获取磁盘缓存大小diskCache.totalCost
  • 获取磁盘缓存数量diskCache.totalCount
  • 获取内存缓存数量memoryCache.totalCount
  • 获取内存缓存数量memoryCache.totalCount
_musicCache.diskCache.totalCost;
_musicCache.diskCache.totalCount;
_musicCache.memoryCache.totalCost;
_musicCache.memoryCache.totalCount;

增删改查的一些方法

  • 封装均采用不阻塞线程带block的方法,对应还有不带block的方法,会阻塞当前线程,直到存取完成
  • 获取文件路径的方法没有,自己看源码简单写了一个
// 存音乐文件,不会阻塞当前线程,操作完成后回调
- (void)saveMusicFile:(id)file forKey:(NSString *)key withBlock:(void(^)(void))block {
    [_musicCache setObject:file forKey:key withBlock:block];
}

// 取音乐文件,不会阻塞当前线程,操作完成后回调
- (void)getMusicFileForKey:(NSString *)key withBlock:(void(^)(NSString *key, id<NSCoding> object))block {
    [_musicCache objectForKey:key withBlock:block];
}

// 判断是否存在这个key
- (void)containsMusicForKey:(NSString *)key withBlock:(void (^)(NSString *key, BOOL contains))block {
    [_musicCache containsObjectForKey:key withBlock:block];
}

// 根据key获取文件路径
- (NSString *)getMusicFilePathWithKey:(NSString *)key {
    dispatch_semaphore_t _lock = [_musicCache.diskCache valueForKey:@"_lock"];
    YYKVStorage *_kv = [_musicCache.diskCache valueForKey:@"_kv"];
    if (!_kv) return nil;
    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
    YYKVStorageItem *item = [_kv getItemForKey:key];
    dispatch_semaphore_signal(_lock);
    NSString *filePath = [NSString stringWithFormat:@"%@/%@",[self musicFilePath],item.filename];
    return filePath;
}

// 删除音乐文件,不会阻塞当前线程,操作完成后回调
- (void)removeMusicFileForKey:(NSString *)key withBlock:(void(^)(NSString *key))block {
    [_musicCache removeObjectForKey:key withBlock:block];
}

// 删除所有音乐文件,不会阻塞当前线程,操作完成后回调
- (void)removeAllMusicFilesWithBlock:(void(^)(void))block {
    [_musicCache removeAllObjectsWithBlock:block];
}

// 音乐文件的存储路径
- (NSString *)musicFilePath {
    return [NSString stringWithFormat:@"%@/data",_musicCache.diskCache.path];
}

// 获取所有音乐文件的key
- (NSArray<NSString *> *)musicAllKeys {
    YYKVStorage *kv = [_musicCache.diskCache valueForKey:@"kv"];
    if (!kv) return nil;
    return [NSArray arrayWithArray:[kv _dbGetkeys]];
}
  • 获取所有文件的key并没有给出方法,我们可以简单仿照源码写一个分类
@interface YYKVStorage (CQ)
- (NSMutableArray *)_dbGetkeys;
@end


#import "YYKVStorage+CQ.h"

#if __has_include(<sqlite3.h>)
#import <sqlite3.h>
#else
#import "sqlite3.h"
#endif

@implementation YYKVStorage (CQ)

- (NSMutableArray *)_dbGetkeys {
    NSString *sql = @"select key from manifest where key is not null;";
    if (![self respondsToSelector:@selector(_dbPrepareStmt:)]) return nil;
    sqlite3_stmt *stmt = (__bridge sqlite3_stmt *)([self performSelector:@selector(_dbPrepareStmt:) withObject:sql]);
    if (!stmt) return nil;

    NSMutableArray *filenames = [NSMutableArray new];
    do {
        int result = sqlite3_step(stmt);
        if (result == SQLITE_ROW) {
            char *filename = (char *)sqlite3_column_text(stmt, 0);
            if (filename && *filename != 0) {
                NSString *name = [NSString stringWithUTF8String:filename];
                if (name) [filenames addObject:name];
            }
        } else if (result == SQLITE_DONE) {
            break;
        } else {
            if (self.errorLogsEnabled) NSLog(@"%s line:%d sqlite query error (%d)", __FUNCTION__, __LINE__, result);
            filenames = nil;
            break;
        }
    } while (1);
    return filenames;
}

@end