When and Why Should You Use Nsuserdefaults'S Synchronize() Method

When and why should you use NSUserDefaults's synchronize() method?

There seems to be so much confusion about user defaults. Think of it this way. It's essentially the same as you having a global dictionary available throughout your app. If you add/edit/remove a key/value to the global dictionary, that change is immediately visible anywhere in your code. Since this dictionary is in memory, all would be lost when your app terminates if it wasn't persisted to a file. NSUserDefaults automatically persists the dictionary to a file every once in a while.

The only reason there is a synchronize method is so your app can tell NSUserDefaults to persist the dictionary "now" instead of waiting for the automatic saving that will eventually happen.

And the only reason you ever need to do that is because your app might be terminated (or crash) before the next automatic save.

In my own apps, the only place I call synchronize is in the applicationDidEnterBackground delegate method. This is to ensure the latest unsaved changes are persisted in case the app is terminated while in the background.

I think much of the confusion comes from debugging an app during development. It's not uncommon during development that you kill the app with the "stop" button in the debugger. And many times this happens before the most recent NSUserDefaults changes have been persisted. So I've developed the habit of putting my app in the background by pressing the Home button before killing the app in the debugger whenever I want to make sure the latest updates are persisted.

Given the above summary, let's review your questions:

should it be called every time the user changes the app's settings?

No. As described above, any change is automatically available immediately.

Or should I just trust that the background api is going to handle that?

Yes, trust the automatic persistence with the exception of calling synchronize when your app enters the background.

And does the leaving of the view immediately after a settings change in memory result in that change being lost?

This has no effect. Once you add/edit/delete a key/value in NSUserDefaults, the change is made.

Also, when might a failure to call synchronize() result in user settings not getting changed correctly?

The only time a change can be lost is if your app is terminated before the latest changes have been persisted. Calling synchronize when your app enters the background solves most of these issues. The only remaining possible problem is if your app crashes. Any unsaved changes that have not yet been persisted will be lost. Fix your app so it doesn't crash.

Furthermore, what is the cost (performance, memory or otherwise) of calling this method? I know it involves reading and writing from/to the disk but does that really take that much effort on phones?

The automatic persistence is done in the background and it simply writes a dictionary to a plist file. It's very fast unless you are not following recommendations. It will be slower if you are misusing NSUserDefaults to store large amounts of data.

NSUserDefaults synchronize-method

The purpose of [default synchronize]; is to make the user defaults get written on disk immediately. You don't need to call it explicitly, iOS already does it at appropriate moments. So you can remove that line. In fact, it's a performance problem if you call synchronize every time you set a default.

Prior to iOS 7, the user defaults were always synchronized when the application transitioned into background. As of iOS 7, that is no longer the case, so you might want to call synchronize in your app delegate's applicationDidEnterBackground: or register to the UIApplicationDidEnterBackgroundNotification notification to do that.

From the documentation for -[NSUserDefaults synchronize]:

Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.

Do I need to do synchronize in NSUserDefaults after removing object from keys?

Quoting this post in stackoverflow, the answer from DarkDust:

The purpose of [default synchronize]; is to make the user defaults
get written on disk immediately. You don't need to call it
explicitly, iOS already does it at appropriate moments. So you can
remove that line. In fact, it's a performance problem if you call
synchronize every time you set a default.

Prior to iOS 7, the user defaults where always synchronized when the
application transitioned into background. As of iOS 7 that is no
longer the case so you might want to call synchronize in your app
delegate's applicationDidEnterBackground: or register to the
UIApplicationDidEnterBackgroundNotification notification to do that.

From the documentation for -[NSUserDefaults
synchronize]
:

Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic
synchronization (for example, if your application is about to exit) or
if you want to update the user defaults to what is on disk even though
you have not made any changes.

When and why does NSUserDefaults' synchronize method fail?

In some cases, my app was trying to write a nil value with a certain key, and that was causing synchronize to fail. Now I'm checking that value for nil before the save and the problem is fixed.

Why would I need to synchronize NSUserDefaults if I haven't made changes?

On iOS, this can happen if the user updates something in the Settings app.

On OSX, this used to happen if your preferences were shared with another app, or if system-wide settings (say, the current language) changed. However, NSUserDefaults has been updated since then to handle this automatically, and re-read data if something has changed externally.

Note that for the latter case, the documentation is out of date. I've already filed a bug report about the docs.

NSUserDefaults synchronize

You could call synchronize() but need to be aware of the following wrinkle it has.

Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic
synchronization (for example, if your application is about to exit) or
if you want to update the user defaults to what is on disk even though
you have not made any changes.

Link

In your scenarios, you should call it since you are making changes and system may not trigger the synchronize() method in time and your check might not be valid when coming back to the view controller on back press.

Another approach that I could suggest would be to declare a global array, maybe in App Delegate and set and fetch values to that.

Now when the app is about to terminate, in applicationWillTerminate just save this global array in user defaults, call synchronize() and similarly when app is about launch, fetch the values from user defaults and set the values to your global array.

This way you need to call synchronize() just once and you will also have latest copy of the data as well.

What parameter do I pass into NSUserDefaults.synchronize()?

You need to call synchronize on standardUserDefaults.

NSUserDefaults.standardUserDefaults().synchronize();

Keep in mind that there is no need to call synchronize.

How can `NSUserDefaults synchronize` runs so fast?

On modern operating systems (iOS 8+, macOS 10.10+), NSUserDefaults does not write the file when you call synchronize. When you call -set* methods, it sends an async message to a process called cfprefsd, which stores the new values, sends a reply, and then at some later time writes the file out. All -synchronize does is wait for all outstanding messages to cfprefsd to receive replies.

(edit: you can verify this, if you like, by setting a symbolic breakpoint on xpc_connection_send_message_with_reply and then setting a user default)

nsuserdefaults synchronize method slows down the application

Does using synchronize slows down the calculations?

Yes, absolutely. synchronize writes the current user default values to the disk.

should i use -synchronize method each time i write into nsuserdefaults?.

No absolutely not. If you have a long loop, where you are changing user defaults, the values are saved in memory. It won't mess up your calculations. It is only necessary to save to disk after the loop is done.

synchronize is usually done:

  1. manually, before the app is terminated or sent to background
  2. automatically by the system every few minutes
  3. manually by the program after some important changes are made that you don't want to risk losing in the event of a crash or sudden power off.

In your case, after the long loop, you want to do it for reason 3.

By doing it every time within the loop, you are just unnecessarily writing values to flash, which you likely immediately overwrite.



Related Topics



Leave a reply



Submit