问题
Basically I am using some open source code called OrderedDictionary that is derived from NSMutableDictionary. Then I want to save the ordered dictionary data to NSUserDefaults by adding encode and decode method to the OrderedDictionary class. However, I realized the encode and decode methods are not being called, as a result, the decoded dictionary is no longer ordered. Below is my code:
@interface OrderedDictionary : NSMutableDictionary <NSCopying, NSCoding>
{
NSMutableDictionary *dictionary;
NSMutableArray *array;
}
In the implementation file:
/**
* encode the object
**/
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:dictionary forKey:@"dictionary"];
[coder encodeObject:array forKey:@"array"];
}
/**
* decode the object
*/
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self != nil)
{
dictionary = [coder decodeObjectForKey:@"dictionary"];
array = [coder decodeObjectForKey:@"array"];
}
return self;
}
Quick example code for using this:
dictionary = [[OrderedDictionary alloc] init];
[dictionary setObject:@"one" forKey:@"two"];
[dictionary setObject:@"what" forKey:@"what"];
[dictionary setObject:@"7" forKey:@"7"];
NSLog(@"Final contents of the dictionary: %@", dictionary);
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"myDictionary"] == nil)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:dictionary]
forKey:@"myDictionary"];
}
else
{
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedDictionary = [currentDefaults objectForKey:@"myDictionary"];
if (savedDictionary != nil)
{
OrderedDictionary *oldData = [NSKeyedUnarchiver unarchiveObjectWithData:savedDictionary];
if (oldData != nil)
{
NSLog(@"Final contents of the retrieved: %@", oldData);
}
}
}
The thing is, the final retrievedDictionary does NOT have the original data order and the encode and decode methods are not called at all.
Thanks for any help in advance! :)
回答1:
There's an NSObject method called -classForCoder
that you need to override in OrderedDictionary. From the docs:
classForCoder
Overridden by subclasses to substitute a class other than its own during coding.-(Class)classForCoder
Return Value
The class to substitute for the receiver's own class during coding.Discussion
This method is invoked by NSCoder. NSObject’s implementation returns the receiver’s class. The private subclasses of a class cluster substitute the name of their public superclass when being archived.
That last line is the key. So, in OrderedDictionary.m:
- (Class)classForCoder
{
return [self class]; // Instead of NSMutableDictionary
}
Also, if you're not using ARC, make sure you retain the objects coming back from -decodeObjectForKey
. As rob mayoff mentions below, you shouldn't call [super initWithCoder:]
(or [super encodeWithCoder:'
). I also changed the key strings to avoid collisions.
- (id)initWithCoder:(NSCoder *)coder
{
if (self != nil)
{
dictionary = [[coder decodeObjectForKey:@"OrderedDictionary_dictionary"] retain];
array = [[coder decodeObjectForKey:@"OrderedDictionary_array"] retain];
}
return self;
}
回答2:
You are possibly creating a new OrderedDictionary
with the wrong initializer, initWithDictionary:
. Try this instead:
OrderedDictionary *oldData = [NSKeyedUnarchiver unarchiveObjectWithData: savedDictionary];
if (oldData != nil)
{
NSLog(@"Final contents of the retrieved: %@", oldData);
}
Make sure initWithDictionary:
expects OrderedDictionary
as an argument. My guess is that it expects an NSDictionary
.
Edit: the code to save the defaults should include something like this:
OrderedDictionary *myDict = ...;
NSData* data = [NSKeyedArchiver archivedDataWithRootObject: myDict];
[[NSUserDefaults standardDefaults] setObject: data forKey: @"myDictionary"];
来源:https://stackoverflow.com/questions/10004783/encodewithcoder-is-not-called-in-derived-class-of-the-nsmutabledictionary