【问题标题】:Why is NSUserDefaults unwilling to save my NSDictionary?为什么 NSUserDefaults 不愿意保存我的 NSDictionary?
【发布时间】:2012-03-14 12:50:45
【问题描述】:

我正在使用 KVC 序列化 NSObject 并尝试将其保存到 NSUserDefaults,当我尝试存储 NSDictionary 时,这给了我一个 Attempt to insert non-property value

以下是相关对象的属性,MyClass

@interface MyClass : NSObject
@property (copy,nonatomic) NSNumber* value1;
@property (copy,nonatomic) NSNumber* value2;
@property (copy,nonatomic) NSString* value3;
@property (copy,nonatomic) NSString* value4;
@property (copy,nonatomic) NSString* value5;
@property (copy,nonatomic) NSString* value6;
@property (copy,nonatomic) NSString* value7;
@property (copy,nonatomic) NSString* value8;
@end

当需要保存 MyClass 时,它会出现在此处:

-(void)saveMyClass
{
  NSArray* keys = [NSArray arrayWithObjects:
                @"value1",
                @"value2",
                @"value3",
                @"value4",
                @"value5",
                @"value6",
                @"value7",
                @"value8",
                nil];
  NSDictionary* dict = [self dictionaryWithValuesForKeys:keys];
  for( id key in [dict allKeys] )
  {
    NSLog(@"%@ %@",[key class],[[dict objectForKey:key] class]);
  }
  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  [defaults setObject:dict forKey:[NSString stringWithString:kMyClassKey]];
  [defaults synchronize];
}

产生这个输出:

2012-02-23 19:35:27.518 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString NSNull
2012-02-23 18:38:48.489 MyApp[9709:40b] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{
    value1 = "http://www.google.com";
    value2 = "MyClassData";
    value3 = 8;
    value4 = "<null>";
    value5 = "http://www.google.com";
    value6 = 1;
    value7 = "http://www.google.com";
    value8 = 4SY8KcTSGeKuKs7s;
}' of class '__NSCFDictionary'.  Note that dictionaries and arrays in property lists must also contain only property values.`

如您所见,dict 中的所有对象都是属性列表值,其所有键都是 NSString*。为了执行这个,我缺少什么琐事?还是我应该放弃并使用writeToFile 或类似的?

【问题讨论】:

  • 您只是在打印密钥。价值观呢?
  • 就是这样,我的价值观之一是作为NSNull(由JSONKit 生成的JSON null)潜入其中,这几乎肯定是原因。我还没有找到解决方案。

标签: iphone objective-c nsdictionary nsuserdefaults kvc


【解决方案1】:

如果需要存储;

  • 来自自定义对象的数据,
  • 或自定义对象数组

您可以使用 NSKeyedArchiver 方法。这个方法可以查看leviathan的answer


但是,如果您尝试存储;

  • 包含 NSString 或 NSNumber 的字典(类似于从 JSON 服务响应转换而来的字典),
  • 或此类字典的数组

您不需要使用 NSKeyedArchiver。您可以使用用户默认值。


就我而言,当我从用户默认值中检索字典时,它返回 nil,所以我认为 NSUserDefaults 不愿意保存我的字典。

但是,它被保存了,但是我使用了错误的 getter 方法从用户默认值中检索它;

[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>]

请确保您使用了其中一个;

[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>]

或;

[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>]

在检查任何其他可能的原因之前。

【讨论】:

  • 为什么要默认使用objectForKey?我认为显式转换不是一个好习惯,应该避免。我的评论也解决了完全相同的问题,不是吗?通过对我的评论投反对票(如果你这样做了),你就无法让人们找到一个可行的例子。 @Jaro
  • 1:这不是问题的答案,因为将字典转换为字符串是可能的,并且在这种情况下解决了问题,但不能解决在字典中包含 对象的问题导致字典无法保存在 NSUserDefaults 中。 2:我认为隐式是更好的设计选择。我的原因是要避免您遇到的这种(很多时候是意外的)错误。我不想为 NSUserDefaults 留下类型检查工具。如果您不想这么明确,最好编写 Archiver 方法。
  • 1:如果主要数据是字典,将其转换为字符串是没有用的,不需要,糟糕的编程。我不是说需要转换。 [NSUserDefaults dictionaryForKey:MY_KEY] 根本不转换数据。但是,如果您使用一种类型保存主要数据并尝试使用另一种类型检索它,那么它会返回 nil,因为它从未保存过。主要问题不是没有保存数据,而是导致完全相同的症状。 2:避免拼写错误是IDE和程序员本身的主要问题。编程设计应包括其他关注点,例如单元测试成本、凝聚力、清晰的依赖关系等。
  • 那么您是否将该字典加载为字符串?据我测试,这是可能的。
  • 我又试了一次,还是不行。您可以在下面查看我的测试代码和日志。日志:2015-09-28 13:45:18.159 测试应用[20772:1787919] stringForKey: (null) 2015-09-28 13:45:18.159 测试应用[20772:1787919] dictionaryForKey: { obj = key; } 代码:[[NSUserDefaults standardUserDefaults] setObject:@{@"obj":@"key"} forKey:@"KEY"]; [[NSUserDefaults standardUserDefaults] 同步]; NSLog(@"stringForKey: %@", [[NSUserDefaults standardUserDefaults] stringForKey:@"KEY"]); NSLog(@"dictionaryForKey: %@", [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"KEY"]);
【解决方案2】:

在将字典保存为用户默认值时,我通常会对其进行归档和取消归档。 这样您就不必手动检查 NSNull 值。

只需将以下两种方法添加到您的代码中即可。可能属于某个类别。

- (BOOL)archive:(NSDictionary *)dict withKey:(NSString *)key {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = nil;
    if (dict) {
        data = [NSKeyedArchiver archivedDataWithRootObject:dict];
    }
    [defaults setObject:data forKey:key];
    return [defaults synchronize];
}

- (NSDictionary *)unarchiveForKey:(NSString *)key {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = [defaults objectForKey:key];
    NSDictionary *userDict = nil;
    if (data) {
        userDict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    }
    return userDict;
}

然后您可以像这样归档任何字典(假设该方法在类中可用):

NSDictionary *dict = ...;
[self archive:dict withKey:@"a key of your choice"];

然后像这样稍后再次检索它:

NSDictionary *dict = [self unarchiveForKey:@"a key of your choice"];

【讨论】:

    【解决方案3】:

    建议打印值的 Kevin 的道具,当然其中一个是 NSNull 类型,它不是属性列表值。谢谢!

    我的问题的笨拙解决方案 - 遍历我刚刚使用 dictionaryWithValuesForKeys 如此方便地生成的字典的键并删除 NSNull 类型的键。叹息

    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryWithValuesForKeys:keys]];
    for( id key in [dict allKeys] )
    {
        if( [[dict valueForKey:key] isKindOfClass:[NSNull class]] )
        {
            // doesn't work - values that are entered will never be removed from NSUserDefaults
            //[dict removeObjectForKey:key];
            [dict setObject@"" forKey:key];
        }
    }
    

    【讨论】:

    • 顺便说一句,我讨厌你 JSONKit 和你的 !@#%@#! NSNulls
    • 我确定[dict removeObjectForKey:key]; 实际上不足以解决我的问题,因为现在NSUserDefaults 中的缺失值永远不会被覆盖。相反,我现在在检测到NSNull 时保存一个空字符串,如答案所示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2015-01-05
    • 2015-04-22
    • 1970-01-01
    • 2013-04-03
    相关资源
    最近更新 更多