Skip to content

iOS_advance_cn

guoling edited this page Apr 19, 2024 · 5 revisions

MMKV for iOS/macOS

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Windows / POSIX 平台,一并开源。

iOS/macOS 进阶

MMKV 有一些高级设置,可以使得更符合你的需求。

日志

  • MMKV 默认将日志打印到 console,对线上问题的解决很不便。你可以在 App 启动时接收转发 MMKV 的日志。实现MMKVHandler协议,添加类似下面的代码:

    - (void)mmkvLogWithLevel:(MMKVLogLevel)level file:(const char *)file line:(int)line func:(const char *)funcname message:(NSString *)message {
        const char *levelDesc = nullptr;
        switch (level) {
            case MMKVLogDebug:
                levelDesc = "D";
                break;
                case MMKVLogInfo:
                levelDesc = "I";
                break;
            case MMKVLogWarning:
                levelDesc = "W";
                break;
            case MMKVLogError:
                levelDesc = "E";
                break;
            default:
                levelDesc = "N";
                break;
        }
        // use your own logging tool
        //NSLog(@"[%s] <%s:%d::%s> %@", levelDesc, file, line, funcname, message);
    }

    至于使用哪个客户端日志组件,我们推荐使用 xlog,同样也是开源自微信团队。

  • 如果你不希望 MMKV 打印日志,你可以在 MMKV 初始化时一劳永逸地关掉它(虽然我们强烈不建议你这么做)。
    注意:除非有非常强烈的证据表明MMKV的日志拖慢了App的速度,你不应该关掉日志。没有日志,日后万一用户有问题,将无法跟进。

    [MMKV initializeMMKV:nil logLevel:MMKVLogNone];

数据恢复

  • 在 crc 校验失败,或者文件长度不对的时候,MMKV 默认会丢弃所有数据。你可以让 MMKV 恢复数据。要注意的是修复率无法保证,而且可能修复出奇怪的 key-value。同样地也是实现MMKVHandler协议,添加以下代码:

    - (MMKVRecoverStrategic)onMMKVCRCCheckFail:(NSString *)mmapID {
        return MMKVOnErrorRecover;
    }
    
    - (MMKVRecoverStrategic)onMMKVFileLengthError:(NSString *)mmapID {
        return MMKVOnErrorRecover;
    }

自定义根目录

  • MMKV 默认把文件存放在$(Documents)/mmkv/目录。你可以在 MMKV 初始化时自定义根目录:

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    auto libraryPath = (NSString *) [paths firstObject];
    auto rootDir = [libraryPath stringByAppendingPathComponent:@"mmkv_2"];
    [MMKV initializeMMKV:rootDir];
  • MMKV 甚至支持自定义某个文件的目录:

    auto path = [MMKV mmkvBasePath];
    path = [path stringByDeletingLastPathComponent];
    path = [path stringByAppendingPathComponent:@"mmkv_3"];
    auto mmkv = [MMKV mmkvWithID:@"test/case1" relativePath:path];

加密

  • MMKV 默认明文存储所有 key-value,依赖 iOS/macOS 系统的沙盒机制保证文件加密。如果你担心信息泄露,你可以选择加密 MMKV。

    NSData *cryptKey = [@"My-Encrypt-Key" dataUsingEncoding:NSUTF8StringEncoding];
    auto mmkv = [MMKV mmkvWithID:@"MyID" cryptKey:cryptKey];
  • 你可以更改密钥,也可以将一个加密 MMKV 改成明文,或者反过来。

    NSString *mmapID = @"testAES_reKey";
    // an unencrypted MMKV instance
    MMKV *kv = [MMKV mmkvWithID:mmapID cryptKey:nullptr];
    
    NSData *key_1 = [@"Key_seq_1" dataUsingEncoding:NSUTF8StringEncoding];
    // change from unencrypted to encrypted
    [kv reKey:key_1];
    
    NSData *key_2 = [@"Key_seq_2" dataUsingEncoding:NSUTF8StringEncoding];
    // change encryption key
    [kv reKey:key_2];
    
    // change from encrypted to unencrypted
    [kv reKey:nullptr];

自动清理

  • MMKV 默认缓存所有访问过的 MMKV 实例, 并在收到 memory warning 的时候清理他们的内部缓存。不过 MMKV 不会彻底销毁他们,除非手工调用 -[MMKV close]。你可以在 MMKV 初始化后调用 +[MMKV enableAutoCleanUp:] 开启 自动清理逻辑:

    // auto clean up instances that not been accessed in 10 minutes
    [MMKV enableAutoCleanUp:10];

备份 & 恢复

  • MMKV 提供了备份和恢复接口,可用于备份数据到其他目录,并稍后恢复原有数据。

    auto parentPath = [[MMKV mmkvBasePath] stringByDeletingLastPathComponent];
    auto dstPath = [parentPath stringByAppendingPathComponent:@"mmkv_backup"];
    auto rootPath = [parentPath stringByAppendingPathComponent:@"mmkv_2"];
    
    // backup one instance (from customize root path)
    auto ret = [MMKV backupOneMMKV:mmapID rootPath:rootPath toDirectory:dstPath];
    // backup all instances
    auto count = [MMKV backupAll:nil toDirectory:dstPath];
    
    // restore one instance (to customize root path)
    ret = [MMKV backupOneMMKV:mmapID rootPath:rootPath toDirectory:dstPath];
    // restore all instances
    count = [MMKV restoreAll:nil fromDirectory:dstPath];

自动过期

  • v1.3.0 起你可以升级 MMKV 到 key 自动过期特性。注意这是格式不向下兼容的升级操作。一旦升级到 key 自动过期,旧版 MMKV (<= v1.2.16) 将无法正常读写该文件。

  • 全局过期. 最简单的用法是给整个文件设定统一的过期间隔。

    // expire in a day
    [mmkv enableAutoKeyExpire:MMKVExpireInDay]; // MMKVExpireInDay = 24 * 60 * 60

    或者,你可以选择只升级 MMKV,但不设置全局过期间隔。这种情况下,默认每个 key 不过期。

    // enable auto key expiration without global duration
    [mmkv enableAutoKeyExpire:MMKVExpireNever]; // MMKVExpireNever = 0
  • 单独过期. 你可以给每个 key 设置单独的过期间隔,区分于文件的全局过期间隔。注意,你仍然需要先升级 MMKV 为 key 自动过期

    // enable auto key expiration with an hour duration
    [mmkv enableAutoKeyExpire:MMKVExpireInHour]; // MMKVExpireInHour = 60 * 60
    
    // set a key with the file's global expiration duration, aka MMKVExpireInHour
    [mmkv setString:@"some value" forKey:@"key_1"];
    
    // set a special key that expires in two hours
    [mmkv setString:@"some value" forKey:@"key_2" expireDuration:(2 * 60 * 60)];
    
    // set a special key that never expires
    [mmkv setString:@"some value" forKey:@"key_3" expireDuration:MMKVExpireNever];

    或者,你可以选择只升级 MMKV,但不设置全局过期间隔。这种情况下,默认每个 key 不过期。

    // enable auto key expiration without global duration
    [mmkv enableAutoKeyExpire:MMKVExpireNever]; // MMKVExpireNever = 0
    
    // set a key that never expires
    [mmkv setString:@"some value" forKey:@"key_1"];
    
    // set a special key that expires in an hour
    [mmkv setString:@"some value" forKey:@"key_2" expireDuration:MMKVExpireInHour];
  • 过期间隔的单位是秒。MMKV 预定义了一些常用间隔,方便大家使用。你也可以使用任意自定义间隔,例如一周是 7 * 24 * 60 * 60

    typedef NS_ENUM(UInt32, MMKVExpireDuration) {
        MMKVExpireNever = 0,
        MMKVExpireInMinute = 60,
        MMKVExpireInHour = 60 * 60,
        MMKVExpireInDay = 24 * 60 * 60,
        MMKVExpireInMonth = 30 * 24 * 60 * 60,
        MMKVExpireInYear = 365 * 30 * 24 * 60 * 60,
    };

下一步

Clone this wiki locally