【问题标题】:Deep mutable copy of a NSDictionary into a NSMutableDictionary将 NSDictionary 的深层可变副本复制到 NSMutableDictionary
【发布时间】:2012-02-25 19:23:51
【问题描述】:

我有一个属性列表,我将其读入 NSDictionary,我想从中创建一个可变副本到 NSMutableDictionary 中以编辑内容,然后可以通过复制回原始内容来重置可变字典。

NSDictionary *defaultRows;
NSMutableDictionary *rows;

NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
defaultRows = [[[NSDictionary alloc] initWithContentsOfFile: plistPath] objectForKey:@"rows"];

这就是我尝试创建原始字典副本的方式(根据Apple's recommendation)

rows = [NSMutableDictionary dictionaryWithDictionary: defaultRows];

当我尝试更改字典行的内容时:

[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType] removeObjectsAtIndexes:rowsToDelete];

我收到运行时错误*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'

我已经阅读了article here,但想知道是否有更优雅的方法来实现深拷贝?

【问题讨论】:

  • 如果 defaultRows 被声明为 NSMutableDictionary 是否有效?
  • BryanMac,很遗憾没有。我声明了NSMutableDictionary *rows, *defaultRows;,但得到了同样的错误信息。

标签: objective-c ios nsarray nsdictionary


【解决方案1】:

由于您是从属性列表加载defaultRows,因此最简单的方法是使用可变容器反序列化属性列表。

NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
NSData *rowsData = [NSData dataWithContentsOfFile:plistPath];

NSMutableDictionary *rows = [NSPropertyListSerialization propertyListWithData:rowsData
    options:NSPropertyListMutableContainers format:NULL error:NULL];

【讨论】:

  • 这是一个非常酷的方法!但是相关选项不应该是 NSPropertyListMutableContainersAndLeaves 吗?我的意思是,假设他想编辑 NSMutableDictionary 中的数组?否则,我认为即使我的答案也可以正常工作.. :)
  • 他问题中的代码试图修改子容器(顶级字典中的字典内的数组),因此他需要可变容器。如果他还需要可变叶子,他当然可以指定NSPropertyListMutableContainersAndLeaves。您的答案将不起作用,因为它只会将顶级字典创建为可变的;它将子容器创建为不可变的。
  • 很好的答案,罗伯!谢谢!顺便问一下,您知道属性列表读取方法是否可以配置为将标准 XML 文件读取到数组和字典中?目前,我正在解析 XML 文件,但想将它们部分直接读入 NSDictionaries。
  • NSPropertyListSerialization 使用特定的 XML 模式。它不是一个通用的 XML 加载器。您将不得不继续以“艰难的方式”(可能使用NSXMLParser)。
  • 虽然这有助于操作 - 这不是问题的答案。它假定数据来自 Plist 文件。但是,如果我从其他地方获得了这个不可变的 NSDictionary——比如 NSUserDefaults,或者只是 CoreData 实体中的一个属性——然后呢?即使我制作了原始的 mutableCopy,我仍然无法将带有 keyPaths 的 KVC 应用于子容器。如何制作深度可变副本?
【解决方案2】:

你考虑过使用

NSMutableDictionary *rows = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistPath] objectForKey:@"rows"];

【讨论】:

  • 如果重新读取属性列表(而不是创建内存副本),性能会受到什么影响?
  • initWithContentsOfFile: 的文档说“这个字典包含的对象是不可变的,即使字典是可变的。”
  • @AlexR 如果您继续重新阅读内容,我的假设是它总是比将对象放在内存中更糟糕。当然,这并不总是可能的,尤其是。如果你有一个巨大的 plist。因此,谨慎的做法是创建一批所有此类写入,然后一次性完成,而不是在每一步都保存它们。另一方面,如果您正在处理诸如设置视图之类的东西,只要用户在该特定视图中,您就可以始终将对象保存在内存中。我的 2 美分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-25
  • 2016-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多