iOS UserDefaults 落后于保存的内容

iOS UserDefaults falls behind saved content

这种情况有点过于复杂,无法用文字描述,所以我创建了一个最小的演示项目供您下载 运行:

https://github.com/mattneub/DefaultsDemo

您将需要 Xcode 10 个来测试。这是项目 运行s:

时的样子

项目代码似乎有很多不必要的混乱,但那是因为我已经包含了我的 real 应用程序中的最小值 material(a纸牌游戏)来重现我所看到的问题。有一副纸牌和一张纸牌布局,当用户单击“主页”按钮时,我们会收到应用程序将退出活动状态的通知,然后我们将纸牌和纸牌布局保存到 UserDefaults 中。当我们重新启动时,我们检索套牌和卡片布局,用户可以继续玩。

但是它不能正常工作,我将解释。而实际上测试看现象很简单:

  1. 运行模拟器上的工程

  2. 点击随机播放按钮三到五次左右。 (如果你开始 运行 没有牌,请点击 New Deck 按钮。但在我的测试中,这通常不是必需的。)

  3. 点击模拟器上的主页按钮。这使我们保存到 UserDefaults 中。查看 Xcode 控制台;它告诉你有很多卡片在刚刚保存到 UserDefaults 中的牌组中。记住那个数字!

  4. 返回 Xcode,停止该项目并再次 运行,从第 1 步重新开始。当我们启动时,您会被告知一副牌中有多少张牌刚刚从 UserDefaults 中检索到。

不断重复这四个步骤。我遇到的情况是,在大约两到三个循环之后,第 4 步中的数字与第 3 步中的数字不同。我们没有从 UserDefaults 返回我们刚才保存到 UserDefaults 中的同一副牌!

此外,关于这个错误的数字,我可以多说几句:它是我们在早些时候中保存到 UserDefaults 中的卡片组中的卡片数量。就好像 UserDefaults 已经以某种方式悄无声息地损坏了,并且已经停止接受新版本的牌组。

为了说明,我刚刚清理了模拟器并打开了项目,我将 运行 它并告诉你我在控制台中看到的内容(注释解释了我所做的事情):

81 // launch
65
starting fresh 65
62 // shuffle
59 // shuffle
56 // shuffle
saving 56 // Home button

好的,让我们从 Xcode:

再次启动
retrieving 56 // launch
53 // shuffle
50 // shuffle
47 // shuffle
44 // shuffle
saving 44 // Home button

好的,让我们从 Xcode:

再次启动
retrieving 56 // launch

这就是问题所在!我们落后了;我们正在从 上一个 存档中取回套牌,而不是我们刚刚保存的那副牌。 (我实际上可以通过打印出牌组来证明这一点;它有一个 description 属性 就是为了这个目的,实际上我可以 看到 这是来自之前的保存。)

所以问题是:为什么?我知道如何解决它;保存到文件而不是 UserDefaults。但我想知道这里的 UserDefaults 是怎么回事导致了这个问题。我 怀疑 我正在保存到 UserDefaults 中的 objects 之一导致了某种无声损坏,但我不明白这是怎么回事,因为它们只是数据objects.

(顺便说一下,调用 synchronize 不会改变任何内容,并且在任何情况下它都会根据 header 被弃用,尽管尚未正式弃用。)

我要说整个项目都是转移注意力的问题,问题出在你的测试程序

我推测 UserDefaults 保存缓慢:将 UserDefaults 更改为 "take" 需要很长时间,甚至 after 发生变化的每个信号都已发生(synchronize 已返回,didChange 通知已到达,等等)。

因此,我敢打赌您只是 在按下主页按钮和再次 运行 应用程序之间没有足够的时间。要了解情况是否如此,请进行以下更改:不要直接从第 3 步转到第 4 步,而是插入第 3a 步,您数到 10,慢慢。现在这个错误没有表现出来。

(也有人认为在willResignActiveNotification时间保存是错误的。保存的时间是相关数据发生变化的时间!因此,在测试示例中,我们应该在每个结束时保存洗牌和发牌操作——不是在用户点击主页按钮时。)