2012-04-18 24 views
2

這是一個奇怪的一個錯誤時,「變異發送到不可變對象方法」 ......奇怪的添加對象的可變數組

週期性我檢查,如果用戶已經在我的應用程序實現了新的最高分。這裏是我的代碼:

- (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]; 
    } 

} 

有時代碼工作得很好,無論是if和else。有時候雖然我得到以下錯誤:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: 
mutating method sent to immutable object' 

我試過設置斷點並逐行通過程序。崩潰似乎有些隨意,我無法制定一個模式。它通常在第二個崩潰...

[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] setObject:topscoreString forKey:@"topscore"]; 

...線。

對象IS可變。或者至少是這樣。它爲什麼會改變?

任何建議的幫助將不勝感激。

謝謝。

回答

4
[[[mutableBoardsAndScores objectAtIndex:gameBoardIndex] mutableCopy] setObject:topscoreString forKey:@"topscore"]; 

你能測試嗎? (爲gameBoard添加了mutableCopy);

編輯

你可以改變上面:

NSMutableArray *gameBoard = [[mutableBoardsAndScores objectAtIndex:gameBoardIndex] mutableCopy]; 

[gameBoard setObject:topscoreString forKey:@"topscore"]; 

[mutableBoardsAndScores removeObjectAtIndex:gameBoardIndex]; 
[mutableBoardsAndScores insertObject:gameBoard atIndex:gameBoardIndex]; 

你可以試試這個?這可能不是最美麗的解決方案,但我相信它會起作用。

+0

它不再有錯誤,但它也停止更新NSUserDefaults。 – Jon 2012-04-18 08:14:49

+0

我把完整的代碼推到github上:https://github.com/jonwheatley/peg – Jon 2012-04-18 08:15:27

+0

我編輯了我的答案,你能試試嗎? – Manuel 2012-04-18 09:01:11

1

NSUserDefaults不存儲您爲其設置的對象。它存儲數據。實際上,它正在複製。而你從NSUserDefaults檢索的任何集合對象都是不可變的。

編輯說:另外,當您製作集合的可變副本時,它是一個淺拷貝。你會得到一個可變的集合,其中包含與原始集合相同的對象。如果原始集合擁有不可變對象,那麼新集合也是如此。

如果您需要執行屬性列表兼容的對象層次結構的深層副本,請取回可變對象,則可以使用NSPropertyListSerialization。結合+dataWithPropertyList:format:options:error:+propertyListWithData:options:format:error:NSPropertyListMutableContainersNSPropertyListMutableContainersAndLeaves選項。