NSUserDefaults 保存两个数组导致崩溃

NSUserDefaults save two arrays leading to crash

最近在研究NSUserDefaults,做了一个demo如下:

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

然后我尝试编写以下内容,在此 post 期间我将调用 "code2":

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

该演示仍然有效。
但是,当我用以下代码替换 "code2" 时,演示崩溃了:

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

如您所见,区别是关键。
为什么会崩溃?

从 NSUserDefaults 返回的数组和字典总是不可变的,即使你设置的是可变的。你必须调用 -mutableCopy.

NSUserDefaults可以存储NSMutableArrays,但会将它们变成不可变数组。

NSDictionaryNSMutableDictionary 也是如此。

这意味着如果你想将一个对象添加到你刚刚从 NSUserDefaults 中提取的数组中,你必须首先使其可变,例如使用 -mutableCopy,或者 -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];

您现在可以毫不费力地修改数组 arr,并且您可以像以前一样保存它。如果您之后检索它,它将是修改后的数组。但是要小心,arraysdictionariesNSUserDefaults 中获取时总是不可变的。每次要从 NSUserDefaults.

修改数组或字典时,都必须这样做 "trick"

编辑:在测试你的代码之后,我唯一的假设是你的无崩溃数组在你检索它时只是 nil 。使用断点调试来验证这一点,但我接近 101% 确定。

EDIT2:特洛伊木马比我更快!

试试这个:

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

正如其他人指出的那样,您从 NSUserDefaults 返回的数组是不可变的,因此在对它们调用 addObject: 时会抛出异常,但是如果数组是 nil,因为对 nil 对象的调用方法(发送消息)被静默忽略。

因此我相信您的 code2 可以正常工作,因为对象 @"activity" 在用户默认值中不存在,而 @"movie" 存在。

userDefault 中的对象不可变。试试这个

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

或者如果您喜欢使用 id 并首先检查其 class 以防止崩溃:

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.. :)