1
votes

I can't figure out what is causing this. Basically, a few different 'tasks' are colliding with each other in my app. When i press a button, it runs this code just fine:

PalAppDelegate *dataCenter = (PalAppDelegate *)[[UIApplication sharedApplication] delegate];




[dataCenter.colourPalettesContainer addObject:[NSNumber numberWithInt:5]];

It can do this as many times as i like. But when i perform another task (and theres a few which cause this to happen), which involves this code for example:

PalAppDelegate *dataCenter = (PalAppDelegate *)[[UIApplication sharedApplication] delegate];

[dataCenter.colourPalettesContainer removeObjectAtIndex:touchDownID];

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:dataCenter.colourPalettesContainer forKey:@"container"];
[prefs synchronize];

and then:

dataCenter.colourPalettesContainer = [prefs objectForKey:@"container"];

When i run the first code again after this, it causes a crash with the "mutating method sent to immutable object" error. How can i stop this?

EDIT: So i've found out the problem from some answers below. Does anybody have a different method of doing this which they'd suggest?

3
Your code is too deeply nested for humans to understand easily. I suggest you break your addObject statement into 5 or 10 sub-statements. Then if something breaks, you'll have a chance of figuring out where it is. - Rayfleck
It only broke since i added the saving to preferences line in. It's not related to my deeply nested Array. I'll remove some of the parts from that to make it easier to digest. - Andrew
You say "when I run the first code again after this, it crashes" - meaning somewhere in the nested array, right? So the problem is you don't know where in all of that assigning the problem is. I have found through much painful experience that highly complex statements like that don't save you any compile or performance time, don't make the code easier to maintain or debug, and so are not worth the time to construct them in the first place. I'm not criticising, just offering some friendly advice. - Rayfleck
The answers don't make much sense, frankly. Post the backtrace of the crash. - bbum
My answer made more sense before Andrew edited his question. He's adding a bunch of mutable collections to NSUserDefaults, then trying to call mutating method on an object he fetches back. The question as its currently written doesn't make that clear, but the original did. - Sherm Pendley

3 Answers

6
votes

NSUserDefaults returns an immutable array. You need to make a mutable copy of it when you load it back up:

NSMutableArray *mutableArray = [[prefs objectForKey:@"container"] mutableCopy];
dataCenter.colourPalettesContainer = mutableArray;
[mutableArray release];

You might also have to do some manipulation inside of the array since you were storing NSMutableArrays within it.

2
votes

NSUserDefaults always returns immutable objects, even if what you stored was mutable. To work around this, you need to make a mutable copy. Since -mutableCopy returns an object that the caller owns, it needs to be (auto)released:

dataCenter.colourPalettesContainer = [[[prefs objectForKey:@"container"] mutableCopy] autorelease];

(Edit) I posted some -mutableDeepCopy NSArray & NSDictionary methods a while back, in response to another question. If your problem involves deeper nesting of collections, and you need them all to be mutable, this may help.

0
votes

TO REMOVE AN OBJECT FROM PARTICULAR INDEX OF AN ARRAY. (Swift 3.0)

let fullArray : NSArray = Userdefaults().value(forKey: "YOUR_ARRAY_STRING") as! NSArray
var mutableArray : [AnyObject] = fullArray as [AnyObject]
mutableArray.remove(at: INDEX_TO_REMOVE) //Eg: mutableArray.remove(at: 0)
mutableArray.append(ARRAY_TO_APPEND)