El método de mutación enviado a object inmutable cierra la aplicación

En mi código, cuando el usuario completa un nivel, desbloquea el siguiente nivel (de un total de 15 niveles).

Sin embargo, he notado que, cuando complete el nivel, la primera vez, funciona, sin embargo, cuando vuelvo a intentarlo nuevamente, se bloquea con este error;

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

La parte del código es esta;

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

Si elimino esta línea;

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

Entonces, la aplicación no falla, pero, por supuesto, no desbloquea ningún nivel adicional.

Algunas references a continuación pueden ayudar;

 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 shanetworkingDirector] replaceScene:scene]; } 

En AppDelegate.m, se llaman niveles en esto;

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

Aquí está la línea problemática:

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

Esto le otorga un NSArray inmutable, incluso cuando guardó una matriz mutable en los valores pnetworkingeterminados. mutableCopy llamar a mutableCopy si desea que su matriz siga siendo mutable:

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

Lo mismo vale para la matriz levelsPizzas .

Los valores devueltos por NSUserDefaults son inmutables, incluso si establece un object mutable como el valor. Tienes que copyrlos mutables.

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

El problema es que piensas en NSUserDefaults como otro NSDictionary .

Después de reiniciar la aplicación o la synchronization de almacenamiento, NSUserDefaults puede haber cambiado todos sus objects mutables (por ejemplo, NSMutableArray ) a NSArray .

También cualquier class que almacene en NSUserDefaults que no sean NSNumber,NSString,NSDate,NSArray o NSDictionary desaparecerán.

Por lo tanto, asegúrese de no almacenarlos en NSUserDefaults ni de manejar el caso cuando ocurra este cambio.

En su código solo necesita cambiar:

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

a

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