I've got dictionary stored in NSUserDefaults
and I need to add/remove items in this dictionary.
It bothers me that to do this I have to create mutable copy of entire dictionary, change one element and replace entire dictionary with new copy.
copy = [[defaults objectForKey:@"foo"] mutableCopy];
[copy setObject:… forKey:@"bar"];
[defaults setObject:copy forKey:@"foo"];
and it involves even more copying and re-setting for objects deeper in the hierarchy.
Is there a better way?
I've tried to use [defaults setValue:… forKeyPath:@"foo.bar"]
but that doesn't seem to work (object is not mutable).
I usually create a custom class to hold all of my application preferences. That class can load mutable copies of the userDefaults once, when the program starts, and then handle all of the incremental saves along the way:
MyPreferences.h
@interface MyPreferences
{
NSMutableDictionary allPrefs;
}
@property (readonly) NSMutableDictionary * allPrefs;
- (void)load;
- (void)save;
@end
MyPreferences.m
@implementation MyPreferences
@synthesize allPrefs;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
allPrefs = [[NSMutableDictionary alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[allPrefs release];
[super dealloc];
}
- (void)load
{
// load all mutable copies here
[allPrefs setObject:[[defaults objectForKey:@"foo"] mutableCopy]
forKey:@"foo"];
// ...
}
- (void)save
{
[defaults setObject:allPrefs forKey:@"app_preferences"];
}
@end
I create an instance of this class in my application delegate and then call [myPrefs load]
when my application launches. Any preferences changed while the program is running can be modified through myPrefs
, and then saved by calling [myPrefs save]
as desired:
MyPreferences * myPrefs = [myApplication myPrefs];
[myPrefs setObject:bar forKeyPath:@"allPrefs.foo.bar"];
[myPrefs save];
As an added bonus, you can structure the MyPreferences
class any way you like, bringing the benefits of OO programming to the whole set of preferences. I showed the easy way here, simply using a mutable dictionary, but you can make each preference into a property, and do pre/post processing for more complicated objects like NSColor
, for instance.