Reputation: 29
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
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
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
Reputation: 2684
Try this:
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[userDefault objectForKey:@"movie"]mutableCopy];
Upvotes: 0
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
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