问题
This is a strange one...
Periodically I am checking if the user has achieved a new top score in my app. Here is my code:
- (void) newTopScore
{
// pull old top score from the database
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *getBoardsAndScores = [defaults dictionaryRepresentation];
NSMutableArray *boardsAndScores = [[getBoardsAndScores objectForKey:@"boardsAndScores"] mutableCopy];
NSMutableDictionary *boardAndScoreObject = [[boardsAndScores objectAtIndex:gameBoardIndex] mutableCopy];
NSString *topscore = [boardAndScoreObject objectForKey:@"topscore"];
NSLog(@"topscore in value: %i", [topscore intValue]);
if ([topscore intValue] == 0)
{
NSString *topscoreString = [[NSString alloc] initWithFormat:@"%i", [self countCurrentPegs]];
NSMutableArray *mutableBoardsAndScores = [boardsAndScores mutableCopy];
[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] setObject:topscoreString forKey:@"topscore"];
[defaults setObject:mutableBoardsAndScores forKey:@"boardsAndScores"];
[defaults synchronize];
}
else if ([self countCurrentPegs] < [topscore intValue])
{
NSString *topscoreString = [[NSString alloc] initWithFormat:@"%i", [self countCurrentPegs]];
NSMutableArray *mutableBoardsAndScores = [boardsAndScores mutableCopy];
[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] setObject:topscoreString forKey:@"topscore"];
[defaults setObject:mutableBoardsAndScores forKey:@"boardsAndScores"];
[defaults synchronize];
}
}
Sometimes the code works just fine, both for the if and the else. Sometimes though I get the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]:
mutating method sent to immutable object'
I've tried setting break points and stepping through the program line by line. The crash seems somewhat random and I can't work out a pattern. It usually crashes on the second...
[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] setObject:topscoreString forKey:@"topscore"];
...line.
The object IS mutable. Or at least it was. Why is it changing?
Any help of advice would be very appreciated.
Thanks.
回答1:
[[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] mutableCopy] setObject:topscoreString forKey:@"topscore"];
Could you test this? (added the mutableCopy for the gameBoard);
EDIT
could you change the above to:
NSMutableArray *gameBoard = [[mutableBoardsAndScores objectAtIndex:gameBoardIndex] mutableCopy];
[gameBoard setObject:topscoreString forKey:@"topscore"];
[mutableBoardsAndScores removeObjectAtIndex:gameBoardIndex];
[mutableBoardsAndScores insertObject:gameBoard atIndex:gameBoardIndex];
Could you try this? It might not be the most beautiful solution but i believe it will work.
回答2:
NSUserDefaults does not store the objects you set to it. It stores the data. Effectively, it's making a copy. And any collection object you retrieve from NSUserDefaults will be immutable.
Edited to say: also, when you make a mutable copy of a collection, it's a shallow copy. You get a mutable collection holding the same objects as the original collection. If the original collection held immutable objects, then so will the new collection.
If you need to do a deep copy of a property-list compatible hierarchy of objects, getting mutable objects back, you can use NSPropertyListSerialization. Combine +dataWithPropertyList:format:options:error: with +propertyListWithData:options:format:error: and the NSPropertyListMutableContainers or NSPropertyListMutableContainersAndLeaves options.
来源:https://stackoverflow.com/questions/10205073/strange-mutating-method-sent-to-immutable-object-error-when-adding-an-object-t