我正在一个类中创建一个字典数组。我想将该数组的副本返回给任何其他需要它的对象。传递给其他对象的这个副本需要在不修改原始的情况下进行修改。
因此,我在包含“主”数组的类的 getter 方法中使用以下内容:
[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];
但是,这似乎使里面的所有字典都不可变。我怎样才能避免这种情况?
我想我在这里遗漏了一些东西。任何帮助都感激不尽!
我正在一个类中创建一个字典数组。我想将该数组的副本返回给任何其他需要它的对象。传递给其他对象的这个副本需要在不修改原始的情况下进行修改。
因此,我在包含“主”数组的类的 getter 方法中使用以下内容:
[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];
但是,这似乎使里面的所有字典都不可变。我怎样才能避免这种情况?
我想我在这里遗漏了一些东西。任何帮助都感激不尽!
您可以采取的另一种方法是使用 CFPropertyListCreateDeepCopy() 函数(在 CoreFoundation 框架中),为 mutabilityOption 参数传入 kCFPropertyListMutableContainers。代码如下所示:
NSMutableArray* originalArray;
NSMutableArray* newArray;
newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);
这不仅会创建字典的可变副本,而且还会递归地创建这些字典包含的任何内容的可变副本。请注意,这仅在您的字典数组仅包含有效属性列表的对象(数组、数字、日期、数据、字符串和字典)时才有效,因此这可能适用于您的特定情况,也可能不适用。
复制数组将创建数组对象的副本,其中包含对原始内容对象的引用(适当保留。)
根据文档,使用原始数组中项目的副本-initWithArray:copyItems:
初始化新数组。此副本是通过发送原始内容对象创建的,在可变对象的情况下,它将创建一个不可变副本。-copyWithZone:
如果您需要不同的行为(即内容对象的可变副本或内容对象的深层副本),您必须编写自己的便利函数/方法来执行此操作。
一种方法是滥用键值编码来发送mutableCopy
到每个字典和autorelease
每个副本。但这是一个肮脏的,肮脏的黑客,所以不要这样做。事实上,你可能一开始就不应该这样做。
通常,当我看到“字典数组”这个词时,我会想到您正在使用字典作为模型对象的替代品。不要那样做。编写自己的模型类;当您拥有自己的自定义属性和行为方法时,一切都会变得容易得多。(有些事情比其他事情更重要:如果没有适当的模型层,几乎不可能实现 AppleScript 支持。)
一旦你有了真实的模型对象,你就可以NSCopying
在其中实现,而不必担心可变与不可变,因为无论如何你的真实模型类中可能不会有可变性的区别。(我不知道其他人,但我从来没有在我的模型类中做出这样的区分。)然后你可以使用现有的 NSArrayinitWithArray:copyItems:
方法。
Jim 关于向每个元素-initWithArray:copyItems
发送消息是正确的。-copyWithZone:
要获取数组元素的可变副本,您需要向每个元素发送-mutableCopyWithZone:
(或只是-mutableCopy
为了简洁)。这很简单:
NSMutableArray *masterArray = ...
NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]];
for (id anObject in masterArray)
[clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject];
但是,在您对问题的解释中隐藏了一个更深层次的问题:您似乎希望数组及其元素(字典)都是可变的,但是有一些要点需要澄清,尤其是考虑到您的规定“[传递给其他对象的]副本需要修改而不修改原始。” 根据您的确切意思,这可能非常复杂,难以保证和实施。
例如,假设原始数组包含许多可变字典。创建前两个级别的可变副本意味着获得副本的人可以修改自己的数组和数组中的字典,而无需直接更改原始数组或字典。但是,如果字典包含可变对象(如 NSMutableArray、NSMutableString 等),则使用“副本”的代码可以间接修改字典的内容,仅使用对可变副本的引用。
可变副本是“浅”的,意味着只复制第一层,并且副本具有指向与原始结构相同的元素的指针。因此,确保原件和副本之间没有联系(至少,手动)需要扫描整个结构并制作副本。如果某些元素不符合 NSCopying 或 NSMutableCopying,这可能会变得更加复杂。
最简单和最快的解决方案是使用上面的简单代码或只返回对主数组的引用。这需要相信客户端代码不会修改数组,因此这可能并非在所有情况下都有效,但如果您也控制调用代码,这可能更可取。另一方面,如果您确实想要一个完全独立的副本,请考虑使用 NSCoding / keyed 归档:
NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];
基本上,这会将所有内容转换为原始数据字节,然后将其重组为一组新对象。为此,字典中的所有对象都必须符合NSCoding 协议。这可能需要做一些事情,但它是保证对象唯一性的一种优雅的通用方式。这当然有非零的性能成本,但如果你绝对必须保证不会有副作用,它应该可以工作。