【问题标题】:Why NSUserDefaults failed to save NSMutableDictionary in iOS?为什么 NSUserDefaults 无法在 iOS 中保存 NSMutableDictionary?
【发布时间】:2010-10-03 01:52:21
【问题描述】:

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

我收到此错误:

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

【问题讨论】:

    标签: ios objective-c cocoa-touch nsuserdefaults nscoding


    【解决方案1】:

    我发现了一种替代方法,在保存之前,我使用NSKeyedArchiver 对根对象(NSArray 对象)进行编码,它以NSData 结尾。然后使用 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 等将其状态写入字典,然后将其存储。未来的证明。
    【解决方案2】:

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

    仅供参考,属性列表类是 NSDictionaryNSArrayNSStringNSDateNSDataNSNumber。您可以根据用户偏好编写可变子类(如NSMutableDictionary),但您读出的对象将始终是不可变的。

    【讨论】:

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

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

    【讨论】:

    • 键是 NSString。但是值是NSArray,它的元素是一个实现NSCoding的自定义类。看来该解决方案不起作用,因为 UserDefaults 仅支持 6 种类型的类,不考虑 NSCoding。
    【解决方案4】:

    最简单的答案:

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


    解决方案:

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

    好处:

    1. 它将添加 String-Value
    2. 当你的变量值为NULL时,它会添加Empty-Value

    【讨论】:

    • 我遇到了类似的问题:我的键是 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的两个方法。

    我希望这会有所帮助!

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-29
        • 1970-01-01
        • 1970-01-01
        • 2012-02-06
        • 2013-10-09
        • 1970-01-01
        相关资源
        最近更新 更多