Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why NSUserDefaults failed to save NSMutableDictionary in iOS?

I'd like to save an NSMutableDictionary object in NSUserDefaults. The key type in NSMutableDictionary is NSString, the value type is NSArray, which contains a list of object which implements NSCoding. Per document, NSString and NSArray both are conform to NSCoding.

I am getting this error:

[NSUserDefaults setObject: forKey:]: Attempt to insert non-property value.... of class NSCFDictionary.

like image 311
BlueDolphin Avatar asked Jan 23 '09 03:01

BlueDolphin


People also ask

How do you save NSUserDefaults in Objective C?

static func setObject(value:AnyObject ,key:String) { let pref = NSUserDefaults. standardUserDefaults() pref. setObject(value, forKey: key) pref. synchronize() } static func getObject(key:String) -> AnyObject { let pref = NSUserDefaults.

What is NSUserDefaults?

The NSUserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an app to customize its behavior to match a user's preferences. For example, you can allow users to specify their preferred units of measurement or media playback speed.


2 Answers

I found out one alternative, before save, I encode the root object (NSArray object) using NSKeyedArchiver, which ends with NSData. Then use UserDefaults save the NSData.

When I need the data, I read out the NSData, and use NSKeyedUnarchiver to convert NSData back to the object.

It is a little cumbersome, because i need to convert to/from NSData everytime, but it just works.

Here is one example per request:

Save:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

Load:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

The element in the array implements

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

Then in the implementation of CommentItem, provides two methods:

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

Anyone has better solution?

Thanks everyone.

like image 176
BlueDolphin Avatar answered Oct 12 '22 15:10

BlueDolphin


If you're saving an object in user defaults, all objects, recursively, all the way down, must be property list objects. Conforming to NSCoding doesn't mean anything here-- NSUserDefaults won't automatically encode them into NSData, you have to do that yourself. If your "list of object which implements NSCoding" means objects that are not property list objects, then you'll have to do something with them before saving to user defaults.

FYI the property list classes are NSDictionary, NSArray, NSString, NSDate, NSData, and NSNumber. You can write mutable subclasses (like NSMutableDictionary) to user preferences but the objects you read out will always be immutable.

like image 30
Tom Harrington Avatar answered Oct 12 '22 13:10

Tom Harrington