2012-05-20 81 views
0

我有一個單例類,我設置了一個名爲completedLevelsNSMutableDictionaryNSMutableDictionary錯誤地設置對象

這是我如何設置它(在我單身的init法):

 NSString *mainPath = [[NSBundle mainBundle] bundlePath]; 
     NSString *levelConfigPlistLocation = [mainPath stringByAppendingPathComponent:@"levelconfig.plist"]; 
     NSDictionary *levelConfig = [[NSDictionary alloc] initWithContentsOfFile:levelConfigPlistLocation]; 
     completedLevels = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *levels = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *stats = [[NSMutableDictionary alloc]init]; 

     [stats setObject:[NSNumber numberWithBool:NO] forKey:@"levelDone"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"stars"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"time"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"bestTime"]; 

     for (int i = 1; i<=18; i++) { 
      [levels setObject:stats forKey:[NSString stringWithFormat:@"level%d", i]]; 
     } 



     for(int i= 1; i<=15;i++){ 
     NSString *lvlSet = [NSString stringWithFormat:@"levelSet%d", i]; 
      [levelSets setObject:levels forKey:lvlSet]; 
     } 


     NSArray *categoriesArray = [levelConfig objectForKey:@"categoriesArray"]; 

     for (int i=0; i<[categoriesArray count]; i++) { 
      NSString *category = [[levelConfig objectForKey:@"categoriesArray"]objectAtIndex:i]; 
      [completedLevels setObject:levelSets forKey:category]; 
     } 

我想解釋一下我的所作所爲:

我的目的是要建立這種形式的字典:在的的Levelset爲1的整數的情況下

 category =  { 
     levelSet1 ={ 
       level1 ={ 
       bestTime = 0; 
       levelDone = 0; 
       stars = 0; 
       time = 0; 
       }; 
       level2={ 
       bestTime = 0; 
       levelDone = 0; 
       stars = 0; 
       time = 0; 
       }; 
       . 
       . 
       . 
      } 
      levelSet2 ={ 
       level1 ={ 
       bestTime = 0; 
       levelDone = 0; 
       stars = 0; 
       time = 0; 
       }; 
       level2={ 
       bestTime = 0; 
       levelDone = 0; 
       stars = 0; 
       time = 0; 
       }; 
       . 
       . 
       . 
      } 
     . 
     . 
     . 
     } 

%d至15

%d在級別爲1至18的整數情況下。

我有幾個類別,因此有多個上面的例子。

這很好,在調用NSLog時,字典會出現在我的控制檯中,因爲它應該。 的問題,但是,出現的時候我想改變我的字典一些條目顯示在下面的例子:

NSString *category = [[GameStateSingleton sharedMySingleton]getCurrentCategory]; 
    NSString *levelSet = [NSString stringWithFormat:@"levelSet%d",[[GameStateSingleton sharedMySingleton]getSharedLevelSet]]; 
    NSNumber *currentLevel = [NSNumber numberWithInt:[[GameStateSingleton sharedMySingleton]getSharedLevel]]; 
    NSString *levelString = [NSString stringWithFormat:@"level%d", [currentLevel intValue]]; 

    NSMutableDictionary *categories = [[NSMutableDictionary alloc]initWithDictionary: 
    [[GameStateSingleton sharedMySingleton]getCompletedLevels]]; 



    [[[[categories objectForKey:category]objectForKey:levelSet]objectForKey:levelString]setObject:[NSNumber numberWithBool:YES] forKey:@"levelDone"]; 

    [[GameStateSingleton sharedMySingleton]setCompletedLevels:categories]; 
    NSLog(@"%@",[[GameStateSingleton sharedMySingleton]getCompletedLevels]); 

要說明的是:

當玩家與水平做,我想levelDone條目可以更改其值。但是當我之後登錄時,所有類別的所有levelDone條目突然變爲BOOLEAN值1.爲什麼?

------------------- update -----------------------

completedLevels = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *levels = [[NSMutableDictionary alloc]init]; 
     NSMutableDictionary *stats = [[NSMutableDictionary alloc]init]; 

     [stats setObject:[NSNumber numberWithBool:NO] forKey:@"levelDone"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"stars"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"time"]; 
     [stats setObject:[NSNumber numberWithInt:0] forKey:@"bestTime"]; 

     for (int i = 1; i<=18; i++) { 
      NSMutableDictionary *statsCopy = [stats mutableCopy]; 
      [levels setObject:statsCopy forKey:[NSString stringWithFormat:@"level%d", i]]; 
      [statsCopy release]; 
     } 



     for(int i= 1; i<=15;i++){ 
      NSString *lvlSet = [NSString stringWithFormat:@"levelSet%d", i]; 
      NSMutableDictionary *levelsCopy = [levels mutableCopy]; 
      [levelSets setObject:levelsCopy forKey:lvlSet]; 
      [levelsCopy release]; 
     } 


     NSArray *categoriesArray = [levelConfig objectForKey:@"categoriesArray"]; 

     for (int i=0; i<[categoriesArray count]; i++) { 
      NSString *category = [[levelConfig objectForKey:@"categoriesArray"]objectAtIndex:i]; 
      NSMutableDictionary *levelSetsCopy = [levelSets mutableCopy]; 
      [completedLevels setObject:levelSetsCopy forKey:category]; 
      [levelSetsCopy release]; 

     } 

,我檢索和設置保持不變的部分...

__ _ __ _ __ _ __ _ __ _ __SOLUTION_ __ _ __ _ __ _ __ _ __ _ ____

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers); 

我做深副本用這種方法。

+0

爲什麼當一個數組可以正常工作時使用字典,因爲你的'字符串鍵'實際上只是一些文本加上真正的索引? –

回答

5

基本上,問題是你要設置的級別字典,每個級別的同一統計對象:

for (int i = 1; i<=18; i++) { 
     [levels setObject:stats forKey:[NSString stringWithFormat:@"level%d", i]]; 
    } 

相反,你需要將它們各自設置到對象的不同可變副本:

for (int i = 1; i<=18; i++) { 
     NSMutableDictionary *statsCopy = [stats mutableCopy]; 
     [levels setObject:statsCopy forKey:[NSString stringWithFormat:@"level%d", i]]; 
     [statsCopy release]; 
    } 

這應該爲您解決第一部分。但是,您也有與levelSets相同的問題。基本上,每當你添加一個可變字典時,你需要確定你是否試圖指向同一個可變對象(你想讓它們保持同步),還是你想要這個可變對象的副本(因此它們獨立地行動)。當你考慮構建這種樹形結構時,這意味着你需要在每個層面上看這個。

因此,查看剩餘的代碼,您還需要以相同的方式修復您的關卡集和類別,方法是在每個循環內部創建一個可變副本,然後添加該可變副本而不是添加原始對象。而且,由於您要變更詞典內容的內容,因此每次製作包含另一個可變詞典的可變副本時,都需要在每個深度進行復制。

您將不得不選擇深入構建這些東西還是進行深層複製。還有另一個問題: deep mutable copy of a NSMutableDictionary 這涵蓋了很好的可變字典的深層副本。或者,您可以倒置該建築物的結構,以便不用複印,而是建立除stats之外的每個字典,您可以輕鬆地複製該字典。

深度複製創建每個可變條目的副本,直到最遠的葉節點。如果你把NSMutableDictionaries看作一棵樹,樹頂部的樹(在你的情況下是completedLevels),樹葉是你複製的統計NSMutableDictionaries的元素,你需要單獨操作樹的每個元素,無論是它是葉節點或任何中間層。

一個例證可以作出如下,與此表示的MyDictionary的內容:

Top-A: 
     Second-A-1 
     Second-A-2 
Top-B: 
     Second-B-1 
     Second-B-2 

總括來說,你有MyDictionary在頂部,其具有2個鍵(Top-ATop-B),每個的它與一個單獨的NSMutableDictionary對象相關聯。每個NSMutableDictionaries都有2個鍵,每個鍵與一個單獨的對象相關聯。

當您進行的MyDictionary-mutableCopy,其結果是一個新的NSMutableDictionary用2個鍵(Top-ATop-B),每個與一個NSMutableDictionary相關聯。 但是,這些NSMutableDictionary對象實際上是與MyDictionary中的對象相同的對象。這是一個淺層副本,這意味着有一個新的頂層對象(MyDictionary的可變副本),但與新字典中每個鍵關聯的對象與MyDictionary中的鍵關聯的對象相同。因此,如果更改Top-A中的副本與不同的NSMutableDictionary相關聯,則Top-A中的MyDictionary與副本將不再相同。但是,如果更改與Second-A-1相關的值,則它將在MyDictionaryMyDictionary的副本中發生更改,因爲Top-A指向單個對象。

當你做一個深拷貝,該程序會將單獨的每個字典的每一個元素,這意味着MyDictionary副本將有一個單獨的Top-ASecond-A-1Second-A-2Top-B等從存在於MyDictionary的那些,因此,您可以更改任何字典的值(無論多深),而無需擔心更改其他對象。

+0

非常感謝。不幸的是你的解決方案只解決了我一半的問題我做了你所說的,並試圖爲所有其他字典制作可變的副本。但是,在*每個* levelSet的level1中,值都會改變......如果您能夠對此進行說明,我將非常感激。 –

+0

所以基本上,當levelSet1的level1中的值發生變化時,levelSet2,levelSet3等中的level1中的值也會發生變化。 –

+0

我已經更新了我的答案,指出在每個循環中都有相同的問題。每個你想要獨立值的地方,你都需要使用NSMutableDictionary的單獨可變副本,否則如果你在多個地方分配同一個NSMutableDictionary,它們將分別引用相同的對象。 – gaige