NSMutableDictionary错误地设置对象

时间:2012-05-20 14:28:50

标签: objective-c ipad singleton nsmutabledictionary

我有一个单身课程,我在其中设置了一个名为NSMutableDictionary的{​​{1}}。

这是我设置它的方式(在我的单身人士的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];
        }
在levelSet的情况下,

%d是从1到15的整数。

在级别的情况下,

%d是从1到18的整数。

我有几个类别,因此上面有多组示例。

这很好用,在调用NSLog时,字典会出现在我的控制台中。 但是,当我想要更改字典中的某些条目时会出现问题,如下例所示:

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

解释一下:

当玩家完成关卡时,我希望 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条目突然变为BOOLEAN值1.为什么会这样?

------------------- update ----------------------- < /强>

levelDone

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

__ _ __ _ __ _ __ _ __ _ __ 解决方案 _ __ _ __ < EM> _ __ _ __ _ __ _ ____

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

        }

我用这种方法制作了深拷贝。

1 个答案:

答案 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个键,每个键与一个单独的对象相关联。

当您-mutableCopy MyDictionary时,结果是一个新的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-1,{{1 }},Second-A-2等来自Top-B中存在的那些,因此,您可以更改任何字典的值(无论多深),而不必担心更改其他对象。< / p>