ChatGPT解决这个技术问题 Extra ChatGPT

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

我想在 NSUserDefaults 中保存一个 NSMutableDictionary 对象。 NSMutableDictionary 中的键类型是 NSString,值类型是 NSArray,其中包含实现 NSCoding 的对象列表。对于每个文档,NSStringNSArray 都符合 NSCoding

我收到此错误:

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


b
brush51

我发现了一种替代方法,在保存之前,我使用以 NSData 结尾的 NSKeyedArchiver 对根对象(NSArray 对象)进行编码。然后使用 UserDefaults 保存 NSData

当我需要数据时,我读出 NSData,并使用 NSKeyedUnarchiverNSData 转换回对象。

这有点麻烦,因为我每次都需要与 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;
}

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

感谢大家。


您有可以分享的代码示例吗,我一直在尝试在帖子中执行此操作(537044),但没有任何乐趣
这对我很有效。我试图对 NSDictionary 对象的 NSArray 进行编码,其中一些键不是字符串。只需在 NSArray 上使用 NSKeyedArchiver 和 Unarchiver 即可消除“尝试插入非属性值”错误。
我什么时候需要释放加载的对象?没有调用 alloc,所以我认为它是自动关联的?
我们可能需要添加 [默认同步] 来保存
此处发布的解决方案可能会工作一段时间,但是当您决定删除或更改课程时,您会发现自己陷入了混乱。更好的解决方案是让您的对象使用 NSNumbers、Strings 等将其状态写入字典,然后将其存储。未来证明。
S
Sajjad Ali

如果您将对象保存在用户默认值中,则所有对象,递归地,一直向下,必须是属性列表对象。符合 NSCoding 在这里没有任何意义——NSUserDefaults 不会自动将它们编码为 NSData,您必须自己这样做。如果您的“实现 NSCoding 的对象列表”表示不是属性列表对象的对象,那么您必须在保存为用户默认值之前对它们进行处理。

仅供参考,属性列表类是 NSDictionaryNSArrayNSStringNSDateNSDataNSNumber。您可以将可变子类(如 NSMutableDictionary)写入用户首选项,但您读出的对象将始终是不可变的。


我还应该提到,如果键是 NSString,则 NSDictionary 只是一个属性列表对象。通常,您可以将其他对象用作字典键,但在需要属性列表的情况下,键必须是字符串。
当我想将 NSURL 作为对象存储在 NSDictionary 中并将其存储在 NSUserDefaults 中时,我遇到了同样的问题。我不得不将它更改为 NSString,然后才起作用。
伟大的!就我而言,我尝试在用户默认值中使用 NSNumber 作为 NSDictionary 的键。我必须将其更改为 NSString。
这种迟钝的行为是什么导致没有经验的编码人员将他们的整个数据模型写成字典而不是制作正确的数据对象? Apple 编写利用 obj-c 具有自省功能并为我们处理装箱和拆箱这一事实的基础类有多难?
S
Sophie Alpert

您的所有键都在字典 NSString 中吗?我认为他们必须是为了将字典保存到属性列表中。


键是 NSString。但值是 NSArray,其元素是实现 NSCoding 的自定义类。似乎该解决方案不起作用,因为 UserDefaults 仅支持 6 种类型的类,不考虑 NSCoding。
B
Bhavin

最简单的答案:

NSDictionary 只是一个 plist 对象,如果 键是 NSStrings。因此,将 "Key" 存储为 NSStringstringWithFormat

解决方案 :

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

好处 :

它将添加字符串值。当您的变量值为 NULL 时,它将添加空值。


我有一个类似的问题:我的键是 NSNumbers,这在 plist 字典的键中显然是不允许的。
N
Nazariy Vlizlo

您是否考虑过实施 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 的两个方法。

我希望这有帮助!


D
Dan Keen

没有更好的解决方案。另一种选择是将编码对象保存到磁盘 - 但这是做同样的事情。他们都以 NSData 结束,当你想要它回来时,它会被解码。