为什么NSUserDefaults无法在iOS中保存NSMutableDictionary?


145

我想在中保存一个NSMutableDictionary对象NSUserDefaults。中的键类型NSMutableDictionaryNSString,值类型为NSArray,其中包含要实现的对象的列表NSCoding。每份文件,NSStringNSArray两者都符合NSCoding

我收到此错误:

[NSUserDefaults setObject:forKey:]:尝试插入类NSCFDictionary的非属性值。

Answers:


230

我找到了一种替代方法,在保存之前,我使用编码了根对象(NSArray对象)NSKeyedArchiver,以结束NSData。然后使用UserDefaults保存NSData

当我需要数据时,我读出NSData,并用于NSKeyedUnarchiver转换NSData回该对象。

这有点麻烦,因为我NSData每次都需要转换到/从中转换,但它确实可以工作。

这是每个请求的一个示例:

保存:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

加载:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

数组中的元素实现

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

然后在实现中CommentItem,提供了两种方法:

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

有人有更好的解决方案吗?

感谢大家。


4
您有可以共享的代码示例吗,我一直在尝试在邮政(537044)中进行此操作,但没有喜悦
Anthony Main

这对我来说很好。我试图对NSDictionary对象的NSArray进行编码,其中某些键不是字符串。只需在NSArray上使用NSKeyedArchiver和Unarchiver即可摆脱“尝试插入非属性值”错误。
约翰·赖特

什么时候需要释放已加载的对象?没有调用alloc,所以我认为它是自动释放的?
Marc 2012年

7
我们可能需要添加[默认同步]进行保存
thanhbinh84'9

此处发布的解决方案可能会工作一段时间,但是当您决定删除或更改课程时,您会发现自己陷入混乱。更好的解决方案是使对象使用NSNumbers,Strings等将其状态写入字典,然后将其存储。未来的证明。
汤姆·安德森

105

如果要使用用户默认值保存对象,则从头到尾递归地所有对象都必须是属性列表对象。符合NSCoding在这里并不意味着什么- NSUserDefaults不会自动将它们编码为NSData,您必须自己做。如果“实现的对象列表NSCoding”表示不是属性列表对象的对象,那么在保存为用户默认值之前,您必须对它们进行一些处理。

FYI财产清单类NSDictionaryNSArrayNSStringNSDateNSData,和NSNumber。您可以NSMutableDictionary为用户首选项编写可变的子类(例如),但您读出的对象始终是不可变的。


61
我还应该提到,如果键是NSStrings,则NSDictionary只是属性列表对象。通常,您可以将其他对象用作字典键,但是在需要属性列表的情况下,键必须是字符串。
汤姆·哈灵顿

当我想将NSURL作为对象存储在NSDictionary中并将其存储在NSUserDefaults中时,遇到了同样的问题。我不得不将其更改为NSString,但效果不佳。
NicTesla 2011年

1
大!就我而言,我尝试使用NSNumber作为用户默认值中NSDictionary的键。我必须将其更改为NSString。
2012年

1
这种迟钝的行为会导致缺乏经验的编码人员将整个数据模型编写为字典,而不是创建适当的数据对象?Apple编写利用obj-c具有自省性并为我们处理装箱和拆箱的事实的基础类,对苹果来说有多难?
ArtOfWarfare 2013年

14

您的所有键都在字典NSStrings中吗?我认为必须将其保存到属性列表中。


1
密钥是NSString。但是值是NSArray,其元素是实现NSCoding的自定义类。看来该解决方案无效,因为UserDefaults仅支持6种类型的类,而不考虑NSCoding。
BlueDolphin

8

最简单的答案:

NSDictionary如果键是NSStrings,则它只是一个plist对象。因此,存储的“钥匙”为用。NSStringstringWithFormat


解决方案:

NSString *key = [NSString stringWithFormat:@"%@",[dictionary valueForKey:@"Key"]];

好处:

  1. 它将添加String-Value
  2. 当您的Variable值为时,它将添加Empty-ValueNULL

2
我有一个类似的问题:我的密钥是NSNumbers,显然在plist字典的密钥中是不允许的。
马克·保利

5

您是否考虑过执行《NSCoding议定书》?这将允许您使用通过实现的两种简单方法在iPhone上进行编码和解码NSCoding。首先,您需要将添加NSCoding到您的班级。

这是一个例子:

这是在.h文件中

@interface GameContent : NSObject <NSCoding>

然后,您将需要实现NSCoding协议的两种方法。

    - (id) initWithCoder: (NSCoder *)coder
    {
        if (self = [super init])
        {
               [self setFoundHotSpots:[coder decodeObjectForKey:@"foundHotSpots"]];
        }
        return self;
    }

    - (void) encodeWithCoder: (NSCoder *)coder
    {
           [coder encodeObject:foundHotSpots forKey:@"foundHotSpots"];
    }

有关更多信息,请查看NSCoder上的文档。这对于我的项目确实非常方便,如果我关闭了该应用程序,则需要在iPhone上保存该应用程序的状态,然后将其恢复为打开状态。

关键是将协议添加到接口,然后实现一部分的两种方法NSCoding

我希望这有帮助!


0

没有更好的解决方案。另一个选择是将编码对象保存到磁盘上,但是这样做是一样的。它们都以NSData结尾,当您想要返回时,NSData会被解码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.