iOS UserDefaults 落后于保存的内容

Posted

技术标签:

【中文标题】iOS UserDefaults 落后于保存的内容【英文标题】:iOS UserDefaults falls behind saved content 【发布时间】:2018-09-21 15:49:13 【问题描述】:

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

https://github.com/mattneub/DefaultsDemo

您将需要 Xcode 10 进行测试。这是项目运行时的样子:

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

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

    在模拟器上运行项目。

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

    点击模拟器上的主页按钮。这使我们保存到 UserDefaults。查看 Xcode 控制台;它告诉您有许多卡片在刚刚保存到 UserDefaults 中的卡片组中。记住那个号码!

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

不断重复这四个步骤。发生在我身上的是,大约两三个周期后,第 4 步中的数字与第 3 步中的数字不同。我们没有从 UserDefaults 中返回我们刚才保存到 UserDefaults 中的同一套牌!

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

为了说明,我刚刚清理了模拟器并打开了项目,我将运行它并告诉你我在控制台中看到了什么(用 cmets 解释我做了什么):

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 中的一个对象会导致某种静默损坏,但我不明白这是怎么回事,因为它们只是 Data 对象。

(顺便说一句,调用synchronize 不会改变任何事情,并且在任何情况下,它都会根据标题被弃用,尽管尚未正式如此。)

【问题讨论】:

可能就是这样。由于UserDefaults 是异步保存的,可能需要等待一两分钟才能重新运行应用程序。我只是看了你的代码。在willResignActiveNotification 块中设置deckArchive 后尝试调用UserDefaults.standard.synchronize()。也许这可以解决它? 这里的行为相同。这很可能是一个时间问题(甚至可能是一个错误),它破坏了更新默认数据库的工作流程。使用synchronize() 强制更新并在重新启动应用程序之前等待几秒钟似乎可以解决问题。错误是否也出现在设备上? @Pranay 我应该提到调用synchronize 不会改变任何东西,并且它已被弃用(参见标题)。 @matt 啊!我很抱歉。我不知道它已被弃用。尽管如此,这似乎是一个非常奇怪的问题。 @vadian 是的,在设备上也是如此。 【参考方案1】:

我将建议整个项目是一个红鲱鱼,问题在于您的测试程序

我推测 UserDefaults 保存速度很慢:对 UserDefaults 的更改需要很长时间才能“接受”,即使每个信号都发生了更改已发生(synchronize 已返回,didChange 通知已到达,等等)。

因此,我敢打赌,您只是 没有在点击 Home 按钮和再次运行应用程序之间留出足够的时间。要查看可能是这样,请进行以下更改:不要直接从第 3 步转到第 4 步,而是插入第 3a 步,在此您数到 10,慢慢。现在该错误不会自行显现。

(也有人会争辩说,在willResignActiveNotification 时间保存是错误的。保存时间是相关数据更改的时间!因此,在测试示例中,我们应该在每次随机播放结束时保存 -交易操作——不是在用户点击 Home 按钮时。)

【讨论】:

以上是关于iOS UserDefaults 落后于保存的内容的主要内容,如果未能解决你的问题,请参考以下文章

从 UserDefaults 中提取 Picker

什么是使用UserDefaults保存数据的适当事件?

UserDefaults - 使用 Xcode Simulator 时保存不一致

在 UserDefaults 中保存项目数组 - iOS 12

iOS 11 presentViewController 落后于呈现控制器

iOS 13 UserDefaults:在某些设备上启动时应用程序崩溃