使用原始元素的可变副本复制NSArray

时间:2009-07-28 13:13:15

标签: objective-c cocoa nsmutablearray nsarray copying

我正在一个类中创建一个字典数组。我想将该数组的副本返回给要求它的任何其他对象。需要修改传递给其他对象的副本,而无需修改原始文件。

所以我在我的类的getter方法中使用以下内容来保存“master”数组:

[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];

然而,这似乎使所有词典都在不可变。我怎么能避免这个?

我想我在这里遗漏了一些东西。任何帮助将不胜感激!

4 个答案:

答案 0 :(得分:14)

您可以采用的另一种方法是使用CFPropertyListCreateDeepCopy()函数(在CoreFoundation框架中),为mutabilityOption参数传入kCFPropertyListMutableContainers。代码看起来像:

NSMutableArray* originalArray;
NSMutableArray* newArray;

newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);

这不仅可以创建字典的可变副本,而且还可以递归地制作这些字典所包含的任何内容的可变副本。请注意,这仅在您的字典数组仅包含有效属性列表(数组,数字,日期,数据,字符串和字典)的对象时才有效,因此这可能适用于您的特定情况,也可能不适用。 / p>

答案 1 :(得分:6)

复制数组将创建数组对象的副本,并引用原始内容对象(适当保留。)

根据文档,-initWithArray:copyItems:使用原始数组中项目的副本初始化新数组。此副本是通过发送原始内容对象-copyWithZone:创建的,这将在可变对象的情况下创建不可变副本。

如果您需要不同的行为(即内容对象的可变副本或内容对象的深层副本),您必须编写自己的便利功能/方法。

答案 2 :(得分:5)

一种方法是滥用键值编码将mutableCopy发送到每个词典,并autorelease发送到每个副本。但这是一个肮脏,肮脏的黑客,所以不要这样做。实际上,你可能不应该首先这样做。

一般来说,当我看到“词典数组”这个词时,我认为你正在使用词典作为模型对象的替代品。不要那样做。编写自己的模型类;当您拥有自己的自定义属性和行为方法时,一切都变得更加容易。 (有些事情比其他事情更重要:如果没有合适的模型层,实现AppleScript支持几乎是不可能的。)

一旦你拥有真正的模型对象,就可以在其中实现NSCopying,而不需要担心mutable和immutable,因为你可能不会在真正的模型类中有可变性区别。 (我不了解其他人,但我从未在我的模型类中做过这样的区分。)然后你可以使用现有的NSArray initWithArray:copyItems:方法。

答案 3 :(得分:2)

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等),则使用“copy”的代码可以间接修改字典的内容,仅使用对可变副本的引用。

可变副本是“浅的”,意味着只复制第一级,并且副本具有指向与原始结构相同的元素的指针。因此,保证原件和副本之间没有连接(至少手动),需要扫描整个结构并进行复制。如果某些元素不符合NSCopying或NSMutableCopying,则会变得更加复杂。

最简单,最快速的解决方案是使用上面的简单代码或只返回对主数组的引用。这需要相信客户端代码不会修改数组,因此这可能不适用于所有情况,但如果您也控制调用代码,这可能更好。另一方面,如果您确实需要完全独立的副本,请考虑使用NSCoding / keyed archiving:

NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];

基本上,这会将所有内容转换为原始数据字节,然后将其重新构建为一组新对象。为此,字典中的所有对象都必须符合NSCoding protocol。这可能需要做一些工作,但它是保证对象唯一性的优雅通用方法。这当然具有非零的性能成本,但如果你绝对必须保证没有副作用,它应该有效。