问题
This situation is a bit too elaborate to describe in mere words, so I've created a minimal demo project for you to download and run:
https://github.com/mattneub/DefaultsDemo
You will need Xcode 10 to test. This is what the project looks like when it runs:
The project code will appear to have quite a bit of unnecessary kerfuffle, but that is because I've included the minimum material from my real app (a card game) to reproduce the issue I'm seeing. There's a deck of cards and a layout of cards, and the idea is when the user clicks the Home button, we get a notification that the app will resign active, and we save the deck and the card layout into UserDefaults. When we relaunch, we retrieve the deck and the card layout and the user can resume playing.
But it's not working correctly, as I shall explain. And actually testing to see the phenomenon is very simple:
Run the project on the simulator.
Hit the Shuffle button three to five times or so. (If you start to run out of cards, hit the New Deck button. But in my testing, that's not usually necessary.)
Hit the Home button on the simulator. This causes us to save into UserDefaults. Look at the Xcode console; it tells you have many cards are in the deck that was just saved into UserDefaults. Remember that number!
Back in Xcode, stop the project and run it again, starting again at Step 1. As we launch, you are told how many cards are in the deck that was just retrieved from UserDefaults.
Keep repeating those four steps. What happens to me is that after about two or three cycles, the number in step 4 differs from the number in step 3. We are not getting back from UserDefaults the same deck that we saved into UserDefaults a moment ago!
Moreover, I can say something more about this wrong number: it is the number of cards in the deck that we saved into UserDefaults on some earlier occasion. It is as if UserDefaults has become corrupt silently in some way and has stopped accepting new versions of the deck.
To illustrate, I have just cleaned the simulator and opened the project, and I'll run it and tell you what I see in the console (with comments explaining what I did):
81 // launch
65
starting fresh 65
62 // shuffle
59 // shuffle
56 // shuffle
saving 56 // Home button
Okay, let's launch again from Xcode:
retrieving 56 // launch
53 // shuffle
50 // shuffle
47 // shuffle
44 // shuffle
saving 44 // Home button
Fine, so let's launch again from Xcode:
retrieving 56 // launch
That's the bug! We've fallen behind; we're retrieving the deck from the previous save, not the one from the save we just did. (I can actually prove this by printing out the deck; it has a description
property for just this purpose, and I can actually see that it's the deck from the previous save.)
So the question is: why? I know how to work around it; save into files instead of UserDefaults. But I'd like to know what's up with UserDefaults here that's causing this problem. I suspect that one of the objects I'm saving into UserDefaults is causing some sort of silent corruption, but I don't see how that can be, as they are just Data objects.
(By the way, calling synchronize
changes nothing, and in any case it is deprecated according to the header, even though not yet formally so.)
回答1:
I'm going to suggest this entire project is a red herring and that the problem is your testing procedure.
I theorize that UserDefaults saves slowly: it just takes a really long time for a change to UserDefaults to "take", even after every signal has occurred that the change has happened (synchronize
has returned, the didChange
notification has arrived, and so on).
Thus, I'm betting that you're just not allowing enough time between hitting the Home button and running the app again. To see that this might be so, make the following change: Instead of going directly from Step 3 to Step 4, interpose Step 3a, where you count to 10, slowly. Now the bug doesn't manifest itself.
(One could also argue that saving at willResignActiveNotification
time is just wrong. The time to save is when the relevant data changes! Thus, in the test example, we should be saving at the end of every shuffle-and-deal operation — not when the user hits the Home button.)
来源:https://stackoverflow.com/questions/52447225/ios-userdefaults-falls-behind-saved-content