Random problems with NSUserDefaults

I am using NSUserDefaults

to store roughly 100 key / value pairs on the user's iOS device. Each pair represents only a string key and a boolean value. This works great almost all the time. Recently, several users have reported that their application has been "reset". In particular, their application was not reading data from the NSUserDefaults

. I'm trying to figure out how this might happen.

A few notes:

  • I call synchronize

    after every update the default for the user
  • I have no code that clears individual records or all defaults
  • The default values ​​are read in application:didFinishLaunchingWithOptions:

  • Default values ​​are not read when the application is moved from background to foreground

I found some interesting comments in this Loom.com blog post . Sound as NSUserDefaults

backing plist may not be available when the app is restarted in the background. I'm not sure if the running apps are restarted if they crash in the background. However, I'm curious because my app does crash in the background according to my crash reporting service. Also, this failure occurs immediately after receiving a memory warning.

Is it possible to prevent the app from reading custom defaults when restarted in the background after crashing (in the background)?

Any advice on how to diagnose this problem is greatly appreciated!

Edit - more info : It looks like the CoreLocation framework might cause apps to restart in the background after crashing in the background. My app includes several third party ad and analytics SDKs. In fact, this problem began to manifest itself after adding one specific SDK that CoreLocation could use.

+3


source to share


3 answers


iOS does some tricky things to (almost) smoothly encrypt data written to disk, so this kind of error is definitely possible. It may be that the file cannot be decrypted for some reason and is instead deleted, returning NSUserDefaults

.

I don't know what the reason is, but it seems likely to me.

Also, beware of NSUserDefaults

storing data in <Application_Home>/Library

that is not a safe place. It is intended for "files that your application loads, or generates and recreates as needed."

Perhaps the best place to store your data is <Application_Home>/Documents

, which is for data that "cannot be recreated by your application." If your user defaults are important enough to be an issue, then it is classified as "user generated content" and therefore should be stored in the Documents folder.

So, I suggest to opt NSUserDefaults

out since it doesn't meet your needs, and save the data by writing NSDictionary

to the Documents folder using either NSCoding or Binary Plist (make sure you set it to NSPropertyListBinaryFormat_v1_0

as this is not the default and should used on slow flash memory like iOS devices).



Apple has good documentation and sample code for serializing NSCoding and Plist:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Archiving/Articles/creating.html#//apple_ref/doc/uid/20000949-BABGBHCA

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/PropertyLists/SerializePlist/SerializePlist.html#//apple_ref/doc/uid/10000048i-CH7-SW1

You can also use master data that I am using in my application or SQLite. But if you are only saving "hundreds" of settings, I would not go with either of those options. They are usually only a good choice if the data does not fit in RAM. For data that fits in memory, NSCoding and Plist are significantly faster and easier to work with.

And also read "Where should you put your application files": https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

+5


source


I think using NSUserDefaults

100 keys / data to store is probably wrong in the first place from a development perspective.
The most correct way is to store the login / sesistive data in a keychain (where you have encryption for free) and the rest in documents using a serialized plist. In NSUserDefaults

I would store something like the settings file path or the version number of the preference file.
Your app can be restarted after crashing in the background if there is a trigger that is actually a CoreLocation

notification.
I really doubt that it is NSUserDefaults

not available in the background (it is also thread safe), I guess it would be such a big problem that SO would have a lot of similar questions.
The fact that you are getting a background memory warning is probably due to the cause of the crashes. What does the crash log say? can you post it?
What is the memory usage when you suspend your application? Do you have any methods triggered by memory alert warnings or application lifecycle notification?



+3


source


I think using NSUserDefaults

100 keys / values ​​to store is the wrong approach. But if you use NSUserDefaults

100 key / value to store that and you read the defaults in application:didFinishLaunchingWithOptions:

Then you just restore and read it in

- (void)applicationWillEnterForeground:(UIApplication *)application;

      

0


source







All Articles