You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NSUserDefaults存储值存在延迟,如何实现即时存储?

问题分析与解决方案

1. 为什么synchronize()没达到预期效果?

苹果早已在官方文档中标记synchronize()为废弃方法,它的设计从未保证即时将数据写入磁盘——即使调用该方法,系统仍会在后台异步完成磁盘写入操作。当你快速关闭App时,异步写入流程还未完成,就会导致数据丢失。此外,iOS 10及以后,UserDefaults会自动定期同步,手动调用synchronize()不仅无效,还可能引发不必要的性能开销。

2. 代码隐藏的另一个问题:非原子操作

你当前的递增逻辑是「读取当前值→计算新值→写入」,这个流程不是原子的。如果短时间内多次触发金币递增(比如用户快速点击获取金币按钮),可能出现多个线程同时读取到相同的初始值,最终写入的结果会远低于预期的增量。

3. 可靠即时存储的实现方案

方案一:修复原子性问题(基于UserDefaults优化)

通过线程安全的原子类封装金币值的读写,避免竞态条件,同时优化同步逻辑:

import Foundation

// 线程安全的原子整数类
class AtomicInteger {
    private let queue = DispatchQueue(label: "com.yourapp.atomic.coins", attributes: .concurrent)
    private var value: Int
    
    init(initialValue: Int) {
        self.value = initialValue
    }
    
    // 读取当前值(并发安全)
    func get() -> Int {
        return queue.sync { value }
    }
    
    // 递增操作(排他锁保证原子性)
    func increment(by amount: Int) -> Int {
        return queue.sync(flags: .barrier) {
            value += amount
            return value
        }
    }
}

// 全局单例存储金币原子对象,初始化时从UserDefaults读取初始值
let coinsAtomicStore = AtomicInteger(initialValue: UserDefaults.standard.integer(forKey: "mycoins"))

// 安全递增金币并同步到UserDefaults
func addCoins(_ amount: Int) {
    let newValue = coinsAtomicStore.increment(by: amount)
    UserDefaults.standard.set(newValue, forKey: "mycoins")
    
    // 仅在iOS 12及以下临时调用synchronize,iOS 13+依赖系统自动同步
    if #available(iOS 13.0, *) {
        // 无需手动同步,系统会自动处理
    } else {
        (UserDefaults.standard as? NSUserDefaults)?.synchronize()
    }
}

方案二:替换为更可靠的存储方式(推荐)

UserDefaults本质是一个plist文件,仅适合存储低频次修改的配置类数据,并不适合金币这种可能高频更新的数值。若需要绝对可靠的即时存储,建议选用以下方案:

  • 直接写入本地文件:将金币值序列化后写入单独的小型文件,使用Data.write(to:options:)并指定.atomic选项,确保写入操作的原子性(要么完全写入,要么不写入)。
  • Core Data:适合需要持久化的结构化数据,调用save()方法可保证数据即时写入磁盘(只要调用成功)。
  • Keychain:虽然Keychain主要用于存储敏感数据,但它的写入操作是即时且持久的,也可用于存储金币这类需要可靠保存的数值。

方案三:临时应急处理(不推荐长期使用)

若暂时无法替换存储方式,可在App即将退出时再次触发同步:
在AppDelegate的applicationWillTerminate(_:)方法中调用synchronize(),提升写入完成的概率:

func applicationWillTerminate(_ application: UIApplication) {
    UserDefaults.standard.synchronize()
}

注意:如果App被系统强制杀死(如内存不足),applicationWillTerminate可能不会被调用,因此该方案无法保证100%可靠。


内容的提问来源于stack exchange,提问作者Dragounow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 04:06:24