In an app I bound an NSArrayController's content array to a NSUserDefaultsController like this:
[_arrayController bind:NSContentArrayBinding toObject:_userDefaultsController withKeyPath:@"values.anArrayOfDictionaries" options:nil];
This works as expected. If I add objects to the array controller or remove them, the user defaults are updated accordingly. But if I modify the content array in any other way, this seems not to trigger KVO and the user defaults are not updated. I tried various combinations of:
[self.arrayController willChangeValueForKey:...];
...
[self.arrayController didChangeValueForKey:...];
but it did not work. So I ended up doing the following:
[_userDefaults setObject:[_arrayController content] forKey:anArrayOfDictionaries];
This works but I am pretty sure there must be a better way to get the changes I made to the array controller's content array into the user defaults.
Thanks in advance for your help.
Regards, Marc
I don't think that I misuse NSUserDefaults. In the app the user can save different configurations and they will stored in NSUserDefaults as an array of dictionaries. This works as expected. But now I got the request to allow the user to set one of the configurations as the default configuration. So I just wanted to add an additional key to one of the dictionaries in the array to mark it as the default.
To clarify here and as broader background, what I mean by "misuse" here isn't that the API "won't work", it's that when developers often run into problems when they store "important" data in NSUserDefault, particularly large amounts of data. Using NSUserDefaults means that you don't actually control how the data is stored, nor do you have any good way to fix or correct things if/when they go wrong. There are two questions you need to look at here:
-
How large is the data, both in overall size and in element "count". NSUserDefaults is for storing "bytes", not "kilobytes", and "10s", not "1000s".
-
How important is the data to your apps ability to function? Would the user be "angry" if it disappeared, slightly annoyed, or not really notice?
I'm not sure where your data fits in that framework, but that's what you should evaluate.
I also tried "mutableArrayForKeyPath" but even if using this, the user defaults are not updated…
Expanding on what's in "Troubleshooting Cocoa Bindings", KVO doesn't automatically track all changes to the object being observed, particularly when we're dealing with object "inside" other objects. So, for example, "NSArrayController" monitors for changes to the array itself. As you noted, adding and removing is working exactly as you'd expect. However, a modification to one of the object the array referenced does NOT change the array's contents, so there isn't anything to notify.
You can setup more complicated observation patterns and there are more examples of how that can be done in "Cocoa Bindings Programming Topics". However, that isn't necessarily the best option. Looking at what you did here:
[_userDefaults setObject:[_arrayController content] forKey:anArrayOfDictionaries];
NSUserDefaults doesn't have any mechanism for modifying "interior" values, only setting the "top" level value. SO, for example, if you have an array of 100 objects and add 1 object, what NSUserDefaults will actually end up doing is basically:
-
"remove" the "original" array of 100 objects.
-
"add" add the "new" array of 101 objects.
-
Save your entire defaults data set back to disk
...even if you "started" with the same array. Architecturally, the best way to think of NSUserDefaults is as a wrapper around PropertyListSerialization. It produces objects by reading from disk and it saves object by writing them to disk. It has a very simplistic cache so that every "get" doesn't require a read, but it doesn't have any real intelligence on the "write" side. Changing one value inside a container isn't actually any better (or worse) than changing the entire container. Either way, it's going to end up writing "everything".
FYI, This is actually the main problem with using NSUserDefaults as a database for storing large amounts of data. NSUserDefaults is NOT a database. It doesn't have any mechanism for doing incremental modifications and it's not really optimized for "efficient" storage. When you modify NSUserDefaults, it write out an new preference file, then replaces the old file with the new one. This works fine when you're storing "small" amounts of data that change "infrequently". However, if you're storing large amounts of data and changing ANY of it frequently, this can end up generating a LOT of unnecessary I/O.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware