Busen
Busen

Reputation: 29

NSUserDefaults save two arrays leading to crash

Recently I was studying NSUserDefaults, then made a demo as follows:

 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 NSMutableArray *activity_array = [NSMutableArray array];
 NSMutableArray *movie_array = [NSMutableArray array];
 [defaults setObject:activity_array forKey:@"activity"];
 [defaults setObject:movie_array forKey:@"movie"];
 [defaults synchronize];

Then I tried writing the following which I will be calling "code2" for the duration of this post:

 NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
 NSMutableArray *array = [userDefault objectForKey:@"activity"];
 [array addObject:@"123"];

the demo still works.
However the demo crashes when I replace "code2" with the following code:

 NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
 NSMutableArray *array = [userDefault objectForKey:@"movie"];
 [array addObject:@"123"];

As you can see, the difference is the key.
Why does this crash?

Upvotes: 1

Views: 415

Answers (5)

Gil Sand
Gil Sand

Reputation: 6040

NSUserDefaults can store NSMutableArrays, but will turn them into immutable arrays.

Same goes for NSDictionary and NSMutableDictionary.

That means if you want to add an object to an array that you just extracted from the NSUserDefaults, you will have to first make it mutable first, using -mutableCopy for example, or -initWithArray.

NSArray *array = [userDefault objectForKey:@"movie"];

//This works
NSMutableArray *arr = [NSMutableArray alloc]initWithArray:array];

//This works too and is more commonly used.
NSMutableArray *arr = [array mutableCopy];

You can now modify the array arr without any trouble, and you will be able to save it just like you did before. If you retrieve it afterwards, it will be the modified array. But be careful, arrays and dictionaries are always immutable when taken from NSUserDefaults. You will have to do that "trick" everytime you want to modify an array or dictionary from the NSUserDefaults.

EDIT : after testing your code, my only assumption is that your crash-free array is simply nil when you retrieve it. Debug with breakpoints to verify this but I'm close to 101% sure.

EDIT2 : trojanfoe got that faster than I did !

Upvotes: 5

0yeoj
0yeoj

Reputation: 4550

The object from userDefault is not mutable.. Try this

NSMutableArray *arr = (NSMutableArray *)[userDefault objectForKey:@"activity"];

or if you like use id and check its class first to prevent crashing:

id variableName = [userDefault objectForKey:@"activity"];

if ([[variableName class] isEqual:[NSArray class]])
{
     NSMutableArray *arr = [[NSMutableArray alloc] initWithArray:(NSArray *)variableName];
     NSMutableArray *arr = [(NSArray *)variableName mutableCopy];
}
else if ([[variableName class] isEqual:[NSNull class]])
    NSLog(@"no object with key:activity");
else
    NSLog(@"not array");

//Happy coding.. :)

Upvotes: 0

Adarsh G J
Adarsh G J

Reputation: 2684

Try this:

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[userDefault objectForKey:@"movie"]mutableCopy];

Upvotes: 0

trojanfoe
trojanfoe

Reputation: 122391

As others have pointed-out the arrays you get back from NSUserDefaults are immutable, so an exception will be thrown when calling addObject: on them, however that won't occur if the array is nil as calling methods (sending messages) to objects that are nil are silently ignored.

Therefore I believe your code2 works as the object @"activity" doesn't exist in the user defaults, while @"movie" does.

Upvotes: 3

Catfish_Man
Catfish_Man

Reputation: 41801

Arrays and dictionaries returned from NSUserDefaults are always immutable, even if the one you set was mutable. You'll have to call -mutableCopy.

Upvotes: 0

Related Questions