Archiving mutable array - doesNotRecognizeSelector exception
I'm having an "doesNotRecognizeSelector" exception and I suspect that maybe my unarchiver return immutable array intstead of mutable. Am I right ? how should I do the archiving and archiving properly ? (place of exception is show down)
Thanks!!!
NSMutableArray* arr;
- (void) write
{
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] copy];
[archiver encodeObject:copy forKey:@"Key"];
[archiver finishEncoding];
[data writeToFile:[Util DataPath] atomically:YES];
[archiver release];
[data release];
[copyOfPaidPacks release];
}
-(NSMutableArray*) read
{
NSString* DataPath = [Util FilePath];
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:DataPath];
NSKeyedUnarchiver *unarchiver = nil;
if (data != nil)
{
unarchiver = [[NSKeyedUnarchiver alloc]in开发者_如何学PythonitForReadingWithData:data];
if([self.Arr count] <= 0)
{
self.Arr = [unarchiver decodeObjectForKey:@"Key"];
}
}
[unarchiver finishDecoding];
[unarchiver release];
[data release];
return self.arr
}
-(void)B
{
[self write];
NSMutableArray* SecondArr = [self read];
[SecondArr sortUsingSelector:@selector(CompareDate:)]; - > `****THIS IS WHERE I GET THE EXCEPTION`
}
- Edit
Adding the compare method:
- (NSComparisonResult)CompareDate:(T*)p
{
NSComparisonResult result = [self.changeDate compare:p.changeDate];
if(result == NSOrderedDescending)
result = NSOrderedAscending;
else if(result == NSOrderedAscending)
result = NSOrderedDescending;
if(result == NSOrderedSame)
{
result = [self CompareName:p];
}
return result;
}
NSKeyedUnarchiver
does indeed return an immutable NSArray
. If you really want to get an NSMutableArray
, you'd need to call -mutableCopy
on the return value from decodeObjectForKey:
.
This code snippet makes me wonder if you really even need a mutable array though. It looks like you're just sorting the array you get from -read
. Why not just call sortedArrayUsingSelector:
on the immutable NSArray instead?
You already pass an immutable array to the archiver, so why would you expect the unarchiver to return a mutable one then?
If you want a copy to be mutable, then you have to use
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] mutableCopy];
as
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] copy];
will create an immutable copy (at least as far, as you should be concerned. In reality the framework will often use a mutable internal representation for immutable instances, but this is an implementation detail and you should not count on it, as these representations have changed in the past and the Cocoa docs explicitly tell you, to not check for the internal representation).
EDIT: just tested with NSKeyedArchiver myself on iOS 5.0 simulator:
// this returns a mutable instance at runtime!
NSMutableArray* test0 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[NSMutableArray array]]];
// this returns an immutable instance at runtime!
NSArray* test1 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[NSArray array]]];
So your problem is really exclusively caused by using copy instead of mutableCopy, while archiving and unarchiving keeps the correct mutability-attributes of the instances!
I had an issue where archiving a 3-dimensional mutable array (ie, a mutable array of mutable arrays of mutable arrays) would unarchive as a mutable array of immutable arrays of immutable arrays.
The key to fixing this was to subclass NSKeyedUnarchiver and overwrite
- (Class)classForClassName:(NSString *)codedName
{
Class theClass = [super classForClassName: codeName];
if ([codeName isEqualToString: NSStringFromClass(NSArray.class))
{
theClass = NSMutableArray.class;
}
//... more here like NSDictionary
return theClass;
}
精彩评论