NSMutableDictionary错误地设置对象

the*_*tic 0 singleton objective-c nsmutabledictionary ipad

我有一个单例类,我设置了一个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];
        }
Run Code Online (Sandbox Code Playgroud)

我想解释一下我的行为:

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

     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;
               };
              . 
              . 
              .
            }
         .
         . 
         .
       }
Run Code Online (Sandbox Code Playgroud)

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]);
Run Code Online (Sandbox Code Playgroud)

解释一下:

当玩家完成关卡时,我希望该levelDone条目更改其值.但是当我之后记录它时levelDone,所有类别的所有条目突然变为BOOLEAN值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];

        }
Run Code Online (Sandbox Code Playgroud)

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

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

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
Run Code Online (Sandbox Code Playgroud)

我用这种方法做了很多副本.

gai*_*ige 5

基本上,问题是您要将每个级别的级别字典设置为相同的stats对象:

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

相反,您需要将它们设置为对象的不同可变副本:

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

那应该为你解决第一部分.但是,levelSets也存在同样的问题.基本上,每当你添加一个可变字典时,你需要确定你是否试图指向同一个可变对象(你想让它们保持同步),或者你是否想要那个可变对象的副本(因此,它们独立地行动).当你看到构建这种树形结构时,这意味着你需要在每个层面上看这个.

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

您将不得不在深度构建这些东西或进行深层复制之间做出选择.还有一个问题: NSMutableDictionary的深度可变副本 很好地涵盖了可变字典的深层副本.或者,您可以反转结构的构建,以便构建每个字典而不是复制,除了stats您可以轻松复制.

深拷贝会创建每个可变条目的副本,一直到最远的叶节点.如果您将NSMutableDictionaries视为一个树,其顶部是树的基础(在您的情况下为completedLevels),而叶子是您复制的统计数据NSMutableDictionaries的元素,则您希望单独操作树的每个元素,它是叶节点或任何中间节点.

可以如下进行说明,其中表示以下内容MyDictionary:

 Top-A:
        Second-A-1
        Second-A-2
 Top-B:
        Second-B-1
        Second-B-2
Run Code Online (Sandbox Code Playgroud)

回顾一下,你有MyDictionary顶部,它有2个键(Top-A,Top-B),每个键与一个单独的NSMutableDictionary对象相关联.每个NSMutableDictionaries都有2个键,每个键与一个单独的对象相关联.

当你创建一个-mutableCopyMyDictionary,结果是一个NSMutableDictionary带有2个键(Top-A,Top-B)的新键,每个键与NSMutableDictionary相关联. 但是,这些NSMutableDictionary对象实际上与其中的对象相同MyDictionary.这是一个浅表副本,意味着有一个新的顶级对象(可变副本MyDictionary),但与新词典中每个键相关联的对象与键中的对象相同MyDictionary.因此,如果您更改Top-A的副本与其他副本相关联NSMutableDictionary,则Top-Ain MyDictionary和副本将不再相同.但是,如果更改与之关联的值Second-A-1,它将同时更改单个对象MyDictionaryMyDictionary因果Top-A点副本.

当你做一个深拷贝,该程序会将单独的每个字典的每一个元素,这意味着该副本MyDictionary将有一个单独的Top-A,Second-A-1,Second-A-2,Top-B,等从存在于那些MyDictionary,因此,你可以改变的任何值字典(无论多深),不用担心改变其他物体.