发送到不可变对象的变异方法关闭app

时间:2015-05-08 13:31:23

标签: ios objective-c cocos2d-iphone

在我的代码中,当用户完成一个级别时,它会解锁下一级别(总共15个级别)。

然而,我注意到,当我完成关卡时,第一次,它会起作用,然而,当我回到它并再试一次时,它会因此错误而崩溃;

[__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent to immutable object

代码部分是这个;

//Unlock Next Level
                if (levelNumber != 15) {
                    [m_appDelegate.levels replaceObjectAtIndex:levelNumber withObject:[NSNumber numberWithBool:NO]];
                    [m_appDelegate saveLevels];
                }

如果我删除此行;

[m_appDelegate.levels replaceObjectAtIndex:levelNumber withObject:[NSNumber numberWithBool:NO]];

然后应用程序不会崩溃,但当然,它不会解锁任何更高级别。

下面的一些参考资料可能有所帮助;

int levelNumber;
@property (nonatomic, readwrite) int levelNumber;


- (void) actionLockedLevel:(id)sender {
    selectedLevel = ((CCNode*)sender).tag;
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Level Locked" message:@"Level is locked. Pass the previous level first." delegate:nil cancelButtonTitle:@"No" otherButtonTitles:nil];
    [alert show];
}

- (void) actionUnlockedLevel:(id)sender {

    CCScene *scene = [CCScene node];
    TimedLevel *layer = [TimedLevel node];
    layer.levelNumber = ((CCNode*)sender).tag;
    [scene addChild:layer];


    [[CCDirector sharedDirector] replaceScene:scene];
}

在AppDelegate.m中,在此处调用级别;

- (void) loadLevels {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if ([defaults objectForKey:@"LEVELS"] == nil) {

        levels = [[NSMutableArray alloc] initWithObjects:
                  [NSNumber numberWithBool:NO],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],
                  [NSNumber numberWithBool:YES],nil];

    } else {
        self.levels = [defaults objectForKey:@"LEVELS"];
    }

    if ([defaults objectForKey:@"LEVELS_PIZZAS"] == nil) {

        levelsPizzas = [[NSMutableArray alloc] initWithObjects:
                        [NSNumber numberWithInteger:200],
                        [NSNumber numberWithInteger:250],
                        [NSNumber numberWithInteger:300],
                        [NSNumber numberWithInteger:350],
                        [NSNumber numberWithInteger:400],
                        [NSNumber numberWithInteger:450],
                        [NSNumber numberWithInteger:500],
                        [NSNumber numberWithInteger:550],
                        [NSNumber numberWithInteger:600],
                        [NSNumber numberWithInteger:650],
                        [NSNumber numberWithInteger:700],
                        [NSNumber numberWithInteger:800],
                        [NSNumber numberWithInteger:900],
                        [NSNumber numberWithInteger:1000],
                        [NSNumber numberWithInteger:1100],nil];

    } else {

        self.levelsPizzas = [defaults objectForKey:@"LEVELS_PIZZAS"]; 
    }

    if ([defaults objectForKey:@"COLLECTED_PIZZAS"] == nil) {
        self.nCurClickAmounts = 0;
    } else {
        self.nCurClickAmounts = [[defaults objectForKey:@"COLLECTED_PIZZAS"] intValue];
    }
    [defaults synchronize];
}

- (void) saveLevels {
    [[NSUserDefaults standardUserDefaults] setObject:levels forKey:@"LEVELS"];
    [[NSUserDefaults standardUserDefaults] setObject:levelsPizzas forKey:@"LEVELS_PIZZAS"];
    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:nCurClickAmounts] forKey:@"COLLECTED_PIZZAS"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

3 个答案:

答案 0 :(得分:3)

这是有问题的一行:

self.levels = [defaults objectForKey:@"LEVELS"];

即使您将可变数组保存为默认值,也会为您提供不可变NSArray。如果您希望阵列保持可变,则需要调用mutableCopy

self.levels = [[defaults objectForKey:@"LEVELS"] mutableCopy];

同样适用于levelsPizzas数组。

答案 1 :(得分:1)

即使您将可变对象设置为值,NSUserDefaults返回的值也是不可变的。你必须可变地复制它们。

self.levels = [[defaults objectForKey:@"LEVELS"] mutableCopy];

答案 2 :(得分:1)

问题在于您认为NSUserDefaults只是另一个NSDictionary

重新启动应用或存储同步后,NSUserDefaults可能已将所有可变对象(例如NSMutableArray)更改为NSArray

您在NSUserDefaults中存储的非NSNumber,NSString,NSDate,NSArrayNSDictionary的任何课程都将消失。

因此,请确保不将它们存储在NSUserDefaults中,或者在发生此更改时处理该情况。

在您的代码中,您只需要更改:

self.levels = [defaults objectForKey:@"LEVELS"];

self.levels = [[defaults objectForKey:@"LEVELS"] mutableCopy];