Serious problem with NSUserDefaults? stringForKey: returning nil, value is in user defaults

This is weird, and I hope it only is occurring in debug mode. But I have a string saved in user defaults. And I'm noticing every so often user defaults is returning nil. And it is making my app act weird. I just caught the problem like this:


-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
     NSString *code = [userDefaults stringForKey:KEY_HERE];

     if (code == nil)
    {
       return YES;
     }
    return NO;
}


Now....I close the last window several times, and the string does get returned. But I noticed after awhile, stringForKey: returns nil out of nowhere and my app terminates. Then on next launch, stringForKey: returns the actual value again (not nil). Is there a known cause for this?

Restarted my Mac and the problem went away for awhile, but then it came back (not long after). I see this in the release notes:


NSUserDefaults Data Loss Fix

Starting in iOS 9.3, and in subsequent releases of iOS and macOS, NSUserDefaults could fail to load data if more than roughly 250 separate apps (including separate reinstalls of the same app) had been launched since the last reboot. This has been corrected.

---


If anyone else runs into this issue let me know. Not sure what I can do about this, I'm currently holding the value in a global variable after first access. as a workarond. Really don't feel like having to store my preferences in my own plist file. Ahhh

Interesting. There are probably many other similar system limits we (I) don't know about.


You confirm an old lesson here, with detailed rationale: one need to reboot mac or device from time to time !

Yeah, the problem returned though not long after restart. Seems strange that NSUserDefaults would return nil for a string after calling stringForKey: several times with the same key (and returning it proper in previous calls)


I doubt my app is hitting any sort of limit like what is described in the resolved issue (problem returned shortly after restart), I use NSUserDefaults in a pretty typical fashion, large objects aren't stored in there or anything like that. Guess I'll have to keep my eye out for this and see if I can figure out a way to regularly reproduce the issue.

Sometimes when my app starts up in High Sierra, it only sees about 50 of the 250-odd keys in my preferences file. If I quit and restart it, maybe it will be back to normal, maybe not. Very worrisome.

Which versions of XCode and system ?

Xcode 9. macOS 10.13

Glad to know it isn't just me. Definitely concerning.

I'm also experincing horrifying issues with NSUserDefaults in High Sierra: app starts with the correct values from NSUserDefaults, but after a certain amount of value exchanges (a lot of gets and some sets) between NSUserDefaults and my app, NSUserDefaults falls back to default values. This is not always reproducable, but if it happens, it happens at exact same spot in the code. If I change something in de code, the problem moves to another part in de code, so it's not related to one setting.


To see when the fallback exactly happens, you can add an observer on a setting that has been successfully saved to a non-default value, like this (Swift):


override func windowDidLoad() {
     UserDefaults.standard.addObserver(self, forKeyPath: "myKey", options: .new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let change = change {
            print(">> USERDEFAULTS VALUE CHANGED " + change.debugDescription)
        }
    }


The problem happens also in production, I receive alarming reports from users using High Sierra.

Did youy file a bug report ?


You could attach this thread which shows it's not specific to you.

Not yet, because I don't know how to reproduce it in a reliable and simple way. My app is too complex to hand over as an example.

This appears to be quite a serious problem. In a settings window of mine, I display strings that were saved in user defaults on a previous run, and I can clearly see that user defaults returned nil, and the placeholder text of the label remains visible. Quit the app, relaunch, go to the same window, and the user default values are loaded proper.


I'd hope they consider this issue to be high priority. Sadly, I do not have time right now to try isolate the issue in a simple sample project and it is a real goose chase for devs who don't have access to the source code to track this down. Like @elemans my project is too complex to hand over as an example.


Are values cached in memory discarded, and in the next call to access that value NSUserDefaults, they try to get values from the cache instead of reading from disk? The return value of nil from user defaults is clearly bogus, because after quitting and relaunching the app, the correct value is returned, at least at first.


I'm considering filing a bug without a sample to "let them know" in case they don't already, to hopefully nudge them to at least take a look, knowing that they will probably ask for a sample but I can't offer such lengthy volunteer work right now.

At a certain point you have to move further. You can't invest too much time in this kind of untraceable problems for simple stuff that just should work. I lost two days on this, and ended up with writing the preferences for the macOS version of my app to a custom plist. Until now, I have no signs that the same problem occur on iOS or tvOS.

My bug got closed as a duplicate of 34819526. So at least they know about this. Hope next macOS update fixes.

It seems that the NSUserDefaults issue is related to switching / activating / deactivating / moving the main app window. NSWindow writes changes to NSUserDefaults when the autosave property is set. I have two totally different apps with totally different codebases that both suffer from this problem. Still the problem is difficult to reproduce, and the stacktrace that's visible on the moment the problem happens, doesn't provide any clues. Another related bug is this one, also something to do with deactivating the main window: https://openradar.appspot.com/34759647

Anyone able to test this on 10.13.1? Haven't upgraded yet to 10.13.1 yet. I really want to avoid migrating preferences to my own plist that are critical to my app's usability. Hope this isn't one of those bugs that will linger til 10.14

Serious problem with NSUserDefaults? stringForKey: returning nil, value is in user defaults
 
 
Q