0

我有一个单例类,我在其中设置了一个NSMutableDictionary被调用的completedLevels.

这就是我设置它的方式(在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];
        }

我想解释一下我的所作所为:

我的意图是以这种形式创建一个字典:

     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;
               };
              . 
              . 
              .
            }
         .
         . 
         .
       }

在 levelSet 的情况下 %d 是从 1 到 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类别的所有条目都变为布尔值 1。这是为什么呢?

- - - - - - - - - - 更新 - - - - - - - - - - - -

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

        }

我检索和设置它的部分保持不变......

__ _ __ _ __ _ __ _ __ _ __ 解决方案 _ __ _ __ _ __ _ __ _ __ _ ____

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

我用这种方法做了深拷贝。

4

1 回答 1

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 也有同样的问题。基本上,每当您添加一个可变字典时,您都需要确定您是否尝试指向同一个可变对象(您希望它们保持同步),或者您是否想要该可变对象的副本(因此,它们独立运行)。当您考虑构建这种树结构时,这意味着您需要在每个级别上都查看它。

因此,查看其余代码,您还需要以相同的方式修复关卡集和类别,方法是在每个循环内制作一个可变副本,然后添加该可变副本而不是添加原始对象。而且,由于您想要改变字典内容的内容,因此每次制作包含另一个可变字典的可变副本时,您都需要在每个深度制作该副本。

您将不得不在深度构建这些东西或进行深度复制之间做出选择。还有另一个问题: 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-A, Top-B),每个键都与一个单独的 NSMutableDictionary 对象相关联。这些 NSMutableDictionaries 中的每一个都有 2 个键,每个键与一个单独的对象相关联。

当你创建一个-mutableCopyofMyDictionary时,结果是一个NSMutableDictionary带有 2 个键 ( Top-A, Top-B) 的新的,每个键与一个 NSMutableDictionary 相关联。 但是,这些NSMutableDictionary对象实际上与 中的对象相同MyDictionary。这是一个浅拷贝,意味着有一个新的顶级对象( 的可变副本MyDictionary),但与新字典中的每个键关联的对象与与 中的键关联的对象相同MyDictionary。因此,如果您Top-A在副本中更改与不同的 相关联NSMutableDictionary,那么Top-AinMyDictionary和副本将不再相同。但是,如果您更改与 关联的值Second-A-1,它将同时MyDictionary更改MyDictionary因为Top-A指向单个对象。

当您进行深度复制时,该实用程序会单独复制每个字典的每个元素,这意味着副本MyDictionary将具有与 中存在的元素不同的Top-ASecond-A-1Second-A-2Top-BMyDictionary,因此,您可以更改任何字典(无论多深),而不必担心更改其他对象。

于 2012-05-20T14:36:55.760 回答