包含自定义对象的 NSMutableDictionaries 的 NSCoding

Posted

技术标签:

【中文标题】包含自定义对象的 NSMutableDictionaries 的 NSCoding【英文标题】:NSCoding of NSMutableDictionaries containing custom objects 【发布时间】:2010-12-03 09:23:25 【问题描述】:

我试图序列化一个包含NSMutableDictionarySearchEntity 对象(自定义对象),该NSMutableDictionary 包含一组CategoryEntity(自定义对象)类型。

1 SearchEntity<NSCoding> 包含: 1NSMutableDictionary(参数) 参数包含 X CategoryEntities<NSCoding> 只包含字符串和数字。

在 SearchEntity encodeWithCoder 中的这一行 [encoder encodeObject:parameters forKey:kPreviousSearchEntityKey];" 我得到 GDB:Interrupted 每次,没有错误消息、异常等。只是 GDB:Interrupted。

这是SearchEntity中的实现,参数是NSMutableDictionary

#pragma mark -
#pragma mark NSCoding delegate methods

- (void) encodeWithCoder:(NSCoder*)encoder 

    //encode all the values so they can be persisted in NSUserdefaults
    if (parameters) 
        [encoder encodeObject:parameters forKey:kPreviousSearchEntityKey]; //GDB:Interrupted!


- (id) initWithCoder:(NSCoder*)decoder 

    if (self = [super init]) 
    
        //decode all values to return an object from NSUserdefaults in the same state as when saved     
        [self setParameters:[decoder decodeObjectForKey:kPreviousSearchEntityKey]];
    
    return self;

CategoryEntity 也实现了 NSCoding 协议,如下所示:

- (void) encodeWithCoder:(NSCoder*)encoder 

    //encode all the values so they can be persisted in NSUserdefaults
    [encoder encodeObject:ID forKey:kIDKey];
    [encoder encodeObject:text forKey:kTextKey];
    [encoder encodeObject:category forKey:kCategoryKey];
    [encoder encodeObject:categoryIdentifierKey forKey:kCategoryIdentifierKey];


- (id) initWithCoder:(NSCoder*)decoder 

    if (self = [super init]) 

        //decode all values to return an object from NSUserdefaults in the same state as when saved
        [self setID:[decoder decodeObjectForKey:kIDKey]];
        [self setText:[decoder decodeObjectForKey:kTextKey]];
        [self setCategory:[decoder decodeObjectForKey:kCategoryKey]];
        [self setCategoryIdentifierKey:[decoder decodeObjectForKey:kCategoryIdentifierKey]];
    
    return self;

我尝试从 NSUserDefaults 的包装器中对其进行编码,如下所示:

+ (void) setPreviousSearchParameters:(SearchParameterEntity*) entity

    if (entity) 
    
        //first encode the entity (implements the NSCoding protocol) then save it
        NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:entity];
        [[self defaults] setObject:encodedObject forKey:kPreviousSearchKey];
        [[self defaults] synchronize];      
    


+ (SearchParameterEntity*) getPreviousSearchParameters

    //retrieve the encoded NSData object that was saved, decode and return it
    SearchParameterEntity *entity = nil;
    NSData *encodedObject = [[self defaults] objectForKey:kPreviousSearchKey];

    if (encodedObject)
        entity = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];

    return entity;

我在想,当我要求序列化 SearchEntity 时,它会开始序列化“参数” mutableDictionary 对象,NSCoder 将对字典中包含的 CategoryEntities 调用“encode”,它们都会以正确的编码对象响应.

但是我只是在控制台底部得到 GDB:Interrupted。

如何调试?

我的方法错了吗,我应该在 NSData 中包装所有级别的编码吗?

附言。我对包含CategoryEntities 的NSArrays 的ResultEntity 做同样的事情,它编码没有问题,所以我猜NSMutableDictionary 是唯一突出的东西。

【问题讨论】:

你有更多关于崩溃的信息吗? NSKeyed(un)Archiver 支持可变对象。崩溃可能是由其他原因引起的。 尝试在控制台中输入 continue - 在显示崩溃/堆栈跟踪之前,您可能需要这样做几次。 您是否在寻找僵尸对象?我通常发现这种出现在看起来还不错的代码上的莫名其妙的错误是由于僵尸造成的。 你的钥匙是什么?请注意,它们必须是 NSStrings。 An answer to a similar question 列出了一些与您的问题相关的有趣警告:(a)字典的键必须是 NSString 和(b)虽然您可以保留可变对象,但它们将被恢复为不可变的等价物. 【参考方案1】:

您发布的代码似乎没有错误。我已经对您遗漏的一些细节做了一个最佳猜测,我从包含您的代码的 test program 中获得了成功的结果,其中包含足够的样板来表明它正确编码/解码。

(您可以从命令行使用:gcc -framework foundation test.m -o test 编译它并使用:./test 运行。)

关于你的问题,我该如何调试这个,我建议如下方法:

(临时)将您的代码修改为尽可能简单。例如,您可以将 parameters 属性更改为普通的 NSString 并首先验证它是否正常工作。 慢慢增加复杂性,一次引入一个新属性,直到错误再次出现。最终,您将缩小麻烦数据的来源。

唉,如果发生这种情况是因为您的应用程序的其他地方存在一些管理不善的内存,那么调试此代码本身可能无法让您获得任何帮助。尝试(手动)验证您收到的用于编码的每条数据的内存管理是否正确。

如果您已经在使用Core Data,您可以考虑在用户默认值中仅保留 对象 ID 并基于此恢复您的对象图。 (参见:Archiving NSManagedObject with NSCoding)。

【讨论】:

【参考方案2】:

我建议你先绕过 NSMutableArray。让 SearchEntity 只包含一个 CategoryEntity,看看它是否有效。

您发布的代码看起来不错,您可能想给我们更详细的上下文。

对于对象编码,此文件可能会有所帮助:DateDetailEntry

【讨论】:

【参考方案3】:

使用 NSKeyedArchiver 归档对象的问题是您无法编码可变对象。仅限 NSArrayNSDictionaryNSStringNSDateNSNumberNSNumber 的实例strong>NSData(以及它们的一些子类)可以序列化

因此,在您的 SearchEntity 方法 encodeWithCoder: 中,您应该尝试从 NSMutableDictionary 创建 NSDictionary,然后对不可变的进行编码:

if (parameters) 
    NSDictionary *dict = [NSDictionary dictionaryWithDictionary:parameters];
    [encoder encodeObject:dict forKey:kPreviousSearchEntityKey];

同样在initWithCoder: 方法中,尝试从编码的不可变数据中创建 NSMutableDictionary:

NSDictionary *dict = [decoder decodeObjectForKey:kPreviousSearchEntityKey];
     [self setParameters:[NSMutableDictionary dictionaryWithDictionary:dict]];

还要检查 parameters 字典中的所有对象以符合 NSCoding 协议,并确保它们都在其 encodeWithCoder: 方法中仅编码不可变对象。

希望它能解决问题。

【讨论】:

NSMutableDictionary 和 NSMutableArray 都实现了 NSCoding。 同意,但这就是我的问题,完全一样,当我尝试对不可变对象进行编码时,它被修复了。我在文档中发现只能序列化不可变对象。

以上是关于包含自定义对象的 NSMutableDictionaries 的 NSCoding的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义对象保存在包含更多自定义对象的数组中

包含另一个自定义对象数组的自定义对象数组上的 NSPredicate

NSUserDefaults 自定义对象 - 属性列表对格式无效:200(属性列表不能包含“CFType”类型的对象)

从数组中创建集合,其中包含自定义对象的数组

Swift:确定自定义对象数组是不是包含特定字符串[重复]

对值可能包含重复项的自定义对象的 NSMutableDictionary 进行排序