Skip to content

mmkv崩溃 #1692

Closed as not planned
Closed as not planned

Description

@baobaomaomeng

环境

  • MMKV:2.4.0(CocoaPods)
  • 崩溃系统:目前观察到的这组崩溃均发生在 iOS 26 及以上版本
  • 项目使用 Swift 和 Objective-C

问题描述

线上崩溃平台多次上报 EXC_CRASH (SIGABRT),断言信息为:

expireDuration == ExpireNever && "setting expire duration without calling enableAutoKeyExpire() first"

这组崩溃大多被平台标记为后台崩溃。目前无法稳定复现,也没有可用于定位具体写入调用的符号化堆栈。

App 启动时先初始化 MMKV,随后立即对默认实例调用 enableAutoKeyExpire:

MMKV.initialize(rootDir: nil)

let kv = MMKV.default()
let enabled = kv?.enableAutoKeyExpire(
    expiredInSeconds: MMKVExpireDuration.never.rawValue
) ?? false

后续初始化流程中还会重试一次,并记录两次调用的返回值。传入 never(0)是希望默认 key 不过期,仅对部分 key 单独指定过期时间。目前尚未取得受影响会话中这两次调用的返回值,不能确认当时是否开启成功。

设置 expireDuration 的调用

目前项目中主要有两类:

  1. 写入 Double:由用户手动操作触发,例如关闭悬浮窗后记录结束时间。

    MMKV.default()?.set(
        endTime, // TimeInterval / Double
        forKey: key,
        expireDuration: UInt32(duration)
    )
  2. 写入 String:用于异步缓存,可能在 App 后台执行。

    MMKV.default()?.set(
        jsonString,
        forKey: cacheKey,
        expireDuration: MMKVExpireDuration.inDay.rawValue
    )

查看 MMKV 2.4.0 的 iOS 源码后,我们理解 String 写入路径在未开启自动过期时,不会触发上述 expireDuration == ExpireNever 断言。此外崩溃平台抓取到不到符号表,我们无法确定实际触发断言的调用。

目前怀疑可能与 iOS 的 App 预热(prewarming)机制有关:系统在后台启动 App 后,某段代码执行了 MMKV 写入。但这只是根据崩溃分布提出的猜测,尚未确认这些崩溃发生在预热启动期间。即使属于预热启动,我们仍无法解释这条断言为何会被触发。

Activity

  1. github-actions commented on Sep 23, 2026

    @github-actions

    👋 Hello! Thanks for opening an issue.

    Note

    New features, bugfixes, updates and other improvements are all handled mostly by @lingol in his free time.
    To support @lingol , please consider 💖 sponsoring him on GitHub 💖.
    Sponsored issues will be prioritized.

    (If you are already a sponsor, thank you! We will get to this shortly.)

  2. lingol commented on Sep 23, 2026

    @lingol
    Collaborator

    I believe the exception is crystal clear. You somehow call MMKV.default().setXXX() without enableAutoKeyExpire() first. Check the call stacks of your crash report and you will find exactly where it's called.

  3. baobaomaomeng commented on Sep 23, 2026

    @baobaomaomeng
    Author

    I believe the exception is crystal clear. You somehow call MMKV.default().setXXX() without enableAutoKeyExpire() first. Check the call stacks of your crash report and you will find exactly where it's called.
    并不,代码在启用mmkv后立刻调用了enableAutoKeyExpire(),如果是你说的这种情况的话,不能解释为什么崩溃只崩在后台。

  4. baobaomaomeng commented on Sep 23, 2026

    @baobaomaomeng
    Author

    补充下,我查下了这些用户的日志,发现在崩溃的前几分钟都发生过版本更新的情况,在旧版本,后台会执行的set也是走double类型的,新版本我们修改了这个。我不清楚这是不是误报,理论上这个bug应该只发生在旧版本。
    但是旧版本的这个崩溃也很令人摸不着头脑,我只找到了一种情况可以稳定复现这个,在有实况通知的情况下重启手机。在iOS的预热机制下app会在第一次解锁屏幕之前运行,从而enableAutoKeyExpire失败,导致崩溃。线上的情况是该bug崩溃率高达千分之一,我不清楚是否还有其他场景能触发这种预热导致的崩溃。

  5. baobaomaomeng commented on Sep 23, 2026

    @baobaomaomeng
    Author

    I believe the exception is crystal clear. You somehow call MMKV.default().setXXX() without enableAutoKeyExpire() first. Check the call stacks of your crash report and you will find exactly where it's called.

    补充下,我查下了这些用户的日志,发现在崩溃的前几分钟都发生过版本更新的情况,在旧版本,后台会执行的set也是走double类型的,新版本我们修改了这个。我不清楚这是不是误报,理论上这个bug应该只发生在旧版本。 但是旧版本的这个崩溃也很令人摸不着头脑,我只找到了一种情况可以稳定复现这个,在有实况通知的情况下重启手机。在iOS的预热机制下app会在第一次解锁屏幕之前运行,从而enableAutoKeyExpire失败,导致崩溃。线上的情况是该bug崩溃率高达千分之一,我不清楚是否还有其他场景能触发这种预热导致的崩溃。

    以及,为什么崩溃百分百集中在iOS26 及以上,预热机制明明是iOS 15引入的

  6. lingol commented on Sep 23, 2026

    @lingol
    Collaborator

    If enableAutoKeyExpire() fails you should NOT set key-value with expiration.
    I don't know why it fails on prewarm for you provide no logs about it.
    Anyway if it fails on prewarm, you might consider

    • prevent your app from continue running in prewarm
    • or somehow retry enableAutoKeyExpire() after it pass the prewam state.
  7. baobaomaomeng commented on Sep 23, 2026

    @baobaomaomeng
    Author

    If enableAutoKeyExpire() fails you should NOT set key-value with expiration.如果 enableAutoKeyExpire() 失败,则不应设置带过期时间的键值对。 I don't know why it fails on prewarm for you provide no logs about it.我不知道为什么预热会失败,你也没有提供相关日志。 Anyway if it fails on prewarm, you might consider总之,** 如果**预热失败,您可以考虑

    • prevent your app from continue running in prewarm防止您的应用程序在预热阶段继续运行
    • or somehow retry enableAutoKeyExpire() after it pass the prewam state.或者在通过 prewam 状态后以某种方式重试 enableAutoKeyExpire() 。

    我觉得你说的很对,我会先把设置过期时间这个修复。关于预热我目前没有办法判断app的启动是否是 prewarm,我网上找了点资料,试着用[ActivePrewarm]这个环境变量去检测,发现根本没有效果,你有什么好办法吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions