UserDefaults在Swift中没有保存

19

我正在尝试使用UserDefaults来持久地保存布尔值。这是我的代码:

public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
    didSet {
        print("Saving isOffline flag which is now \(isOffline)")
        UserDefaults.standard.set(isOffline, forKey: "isOffline")
        UserDefaults.standard.synchronize()
    }
}

为什么不起作用?这段代码有什么问题?

问题在于当我尝试从UserDefaults中检索"isOffline"键时,我总是得到一个false

我在行的.onChange方法中设置了isOffline(我正在使用Eureka作为创建表单的框架)。标志在应用程序的生命周期内保持正确的值,但当我关闭它时,该值可能以某种方式被删除。


你的代码有什么问题?请给我们展示一个最小可复现代码。 - Hamish
1
还要注意,您不需要每次更改用户默认值时都调用synchronize() - 只有在您特别需要系统立即保存用户默认值时(例如当您的应用程序即将终止时)才需要 - 请参见此问答 - Hamish
你是如何设置 isOffline 的?在哪里设置的? - Hamish
@Hamish 我添加了更多细节。 - Andrea Mario Lufino
我找到了问题。UserDefaults只能在主线程上保存。我将保存isOffline的代码行写在onChange块之外,将其放入NSUserDefaults中,一切都运作正常。 - Andrea Mario Lufino
显示剩余4条评论
4个回答

32

我曾经遇到过同样的问题,问题出在“didSet”块本身。我不知道为什么,但它与userDefaults不兼容 - 它无法正确保存信息,在关闭应用程序后所有更改都会消失。

调用Synchronize()没有帮助。我发现,这个方法已不再必要,并且将来会被弃用(这是UserDefaults类中的注释):

-synchronize已被弃用,并将在未来的版本中使用NS_DEPRECATED宏标记。

通过试验,我发现如果从主线程调用它,它可以正常工作:

public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
    didSet {
        print("Saving isOffline flag which is now \(isOffline)")
        DispatchQueue.main.async {
            UserDefaults.standard.set(isOffline, forKey: "isOffline")
        }
    }
}

如果有人能解释一下为什么它只在主线程上运行而在其他线程上不运行,我会很高兴听到。


2
在运行时,您可以使用UserDefaults对象从用户默认数据库中读取应用程序使用的默认值。 UserDefaults缓存信息以避免每次需要默认值时都打开用户默认数据库。当您设置默认值时,它会同步更改您的进程中的值,并异步更改到持久存储和其他进程中。这可能是由于缓存以及在主线程上进行更改可能导致默认值被立即写入的原因。 - Ariq

6

尝试变更

UserDefaults.standard.set(isOffline, forKey: "isOffline")

为了

UserDefaults.standard.setValue(isOffline, forKey: "isOffline")

不带分发代码


4
你知道为什么set()函数不起作用吗?因为在我的设备上它似乎有点随机。有时它能工作,有时只能工作几个值。有时候则不能。奇怪的是,有时我存储了这些值,然后加载它们,却得到了空值。然后我重新启动应用程序,突然一些值又回来了。使用setValue似乎解决了这些问题。但我真的很想知道为什么! :) - LetzFlow
抱歉,我不知道原因,但很高兴它对我们所有人都有效。苹果的工作方式很神秘 :p - Omar N Shamali
1
是的,使用setValue而不是set问题解决了。 - Ahmed Shendy

2

不要问我为什么,但我在Xcode 14的一个项目中也遇到过这个问题。

经过数小时的调试,我意识到我的密钥长度超过18个字符时,它将不再被保存。然而,我无法在普通项目中复现此问题,所以我猜测它与该项目有关。

但无论如何,如果您遇到此问题,请尝试减少密钥的字符数。


2

请这样做:

public static var isOffline:Bool {
    get {
       return UserDefaults.standard.bool(forKey: "isOffline")
    }
    set(newValue) {
        print("Saving isOffline flag which is now \(isOffline)")
        UserDefaults.standard.set(newValue, forKey: "isOffline")
        UserDefaults.standard.synchronize()
    }
}

2
你的第一个例子不起作用,因为你所谓的“newValue”实际上是属性的旧值。但是,两个例子都没有回答OP的问题:“这段代码有什么问题?” - Hamish
第一个例子也可以工作。我个人测试过了才发布的。 - Venk
1
我已经苦苦挣扎和调试了几天......而所有需要的就是 .synchronize。 - MLBDG
1
然而,如果你查看函数描述 https://developer.apple.com/documentation/foundation/nsuserdefaults/1414005-synchronize : "等待默认数据库中所有挂起的异步更新完成并返回;此方法是不必要且不应使用的。" - tryp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接