我一直在开发一个应用程序,用于在启动应用程序之间存储用户数据(用户名等);我将这些数据存储在UserDefaults中。但是,我最近发现了一个问题:有时候,当我运行应用程序时,我会从UserDefaults.standard.object(forKey:)
获得一些价值,但有时候当我知道有一个事实时我会得到nil
那里的东西。
这对我没有意义。
我在SO上搜索了这个问题的答案,但只找到了this,但没有帮助。
为了测试这个,我制作了一个空的应用程序并将以下内容放在viewDidAppear
中,然后我运行了一次应用程序:
UserDefaults.standard.setValue("aValue", forKey: "aKey")
以上一行只是为了确保实际存储了一个值。然后,我从viewDidAppear
删除了上述行,然后将其放在didFinishLaunchingWithOptions
:
print(UserDefaults.standard.object(forKey: "aKey") as? String)
然后我运行了20次应用程序。以下是print(...)
打印nil
或"aValue"
:
✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓
它在35%的时间内打印nil
,在我看来也相当随意。
我有两个问题:
为什么会发生这种情况,我该如何解决/阻止它?
答案 0 :(得分:1)
如果在视图控制器中didFinishLaunchingWithOptions
之后调用AppDelegate中的viewDidAppear
,则可能会发生这种情况。
建议的方法(由Apple)尽快注册所有键/值对(awakeFromNib
或applicationWillFinishLaunching
)
let defaultValues = ["aKey" : "aValue"]
UserDefaults.standard.register(defaults: defaultValues)
默认值"aValue"
将被考虑,直到第一次被覆盖。
如果您重置应用程序的数据,则会再次读取默认值。
这是最可靠的方式。
PS:切勿将valueForKey:
和setValue:forKey:
与UserDefaults
答案 1 :(得分:1)
将set
放在viewDidAppear
而get
放在didFinishLaunching
中是很疯狂的,因为它们是无关的,你对订单一无所知它们将在何时发生,甚至viewDidAppear
将永远发生(毕竟不是每个视图控制器都会出现)。
将它们放在同一个地方进行测试。例如,我们将它们都放在didFinishLaunching
:
let val = UserDefaults.standard.object(forKey: "aKey") as? String
if val != nil {
print("got it")
} else {
print("didn't get it, setting it")
UserDefaults.standard.set("aValue", forKey: "aKey")
}
您会发现这样可以正常运作。
编辑好的,感谢你发布的电影,我们发现有一个复杂的问题:你在设备上测试了这个。您正在通过反复按Xcode中的“运行”按钮进行测试,而不会以一致的方式停止应用程序,这可能会使Xcode混淆,以便它完全替换,或者在第一次执行时永远不会有机会将默认值写入磁盘地点。相反,运行,切换到设备并点击主页按钮,现在切换回Xcode和停止。现在再跑一次。我想你会发现你会得到更一致的结果。