121

是否有任何内置函数可以让我深拷贝一个NSMutableArray?

我环顾四周,有人说[aMutableArray copyWithZone:nil]作为深拷贝工作。但我试过了,它似乎是一个浅拷贝。

现在我正在使用for循环手动进行复制:

//deep copy a 9*9 mutable array to a passed-in reference array

-deepMuCopy : (NSMutableArray*) array 
    toNewArray : (NSMutableArray*) arrayNew {

    [arrayNew removeAllObjects];//ensure it's clean

    for (int y = 0; y<9; y++) {
        [arrayNew addObject:[NSMutableArray new]];
        for (int x = 0; x<9; x++) {
            [[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];

            NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
            for (int i = 0; i<[aDomain count]; i++) {

                //copy object by object
                NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
                [[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
            }
        }
    }
}

但我想要一个更干净、更简洁的解决方案。

4

6 回答 6

214

正如有关深拷贝的 Apple 文档明确指出的那样:

如果只需要一级深拷贝:

NSMutableArray *newArray = [[NSMutableArray alloc] 
                             initWithArray:oldArray copyItems:YES];

上面的代码创建了一个新数组,其成员是旧数组成员的浅表副本。

请注意,如果您需要深度复制整个嵌套数据结构(链接的 Apple 文档称为真正的深度复制),那么这种方法是不够的。请在此处查看其他答案。

于 2009-03-15T09:18:24.247 回答
63

我知道轻松做到这一点的唯一方法是存档,然后立即取消存档您的阵列。感觉有点像 hack,但实际上在Apple Documentation on copying collections中明确建议,其中指出:

如果你需要一个真正的深拷贝,比如当你有一个数组时,你可以归档然后解归档集合,前提是内容都符合 NSCoding 协议。清单 3 显示了这种技术的一个示例。

清单 3 一个真正的深拷贝

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

问题是您的对象必须支持 NSCoding 接口,因为这将用于存储/加载数据。

斯威夫特 2 版本:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))
于 2009-03-15T04:04:55.373 回答
35

默认情况下,复制提供浅拷贝

这是因为调用与使用默认区域进行复制copy相同。copyWithZone:NULLcopy调用不会导致深层复制。在大多数情况下,它会给你一个浅拷贝,但无论如何它取决于类。对于全面的讨论,我推荐Apple Developer 网站上的Collections Programming Topics 。

initWithArray:CopyItems: 提供一级深拷贝

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSCoding是 Apple 推荐的提供深拷贝的方式

对于真正的深层副本(数组数组),您将需要NSCoding并归档/取消归档对象:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
于 2013-02-23T12:57:53.963 回答
7

对于字典

NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);

对于数组

NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);

于 2015-10-14T10:52:57.583 回答
5

不,框架中没有为此内置任何东西。Cocoa 集合支持浅拷贝(使用copyorarrayWithArray:方法),但甚至不谈论深拷贝的概念。

这是因为“深拷贝”开始变得难以定义,因为您的集合的内容开始包含您自己的自定义对象。“深拷贝”是否意味着对象图中的每个对象都是相对于原始对象图中的每个对象的唯一引用?

如果有一些假设的NSDeepCopying协议,您可以设置它并在所有对象中做出决定,但不幸的是没有。如果您控制了图中的大部分对象,您可以自己创建此协议并实现它,但您需要根据需要向 Foundation 类添加一个类别。

@AndrewGrant 的回答建议使用键控归档/取消归档是为任意对象实现此目的的一种性能不佳但正确且干净的方法。这本书甚至到目前为止建议为所有支持深度复制的对象添加一个类别。

于 2013-11-02T15:26:30.673 回答
2

如果尝试实现 JSON 兼容数据的深层复制,我有一个解决方法。

只需NSData使用然后重新创建 JSON 对象,这将创建一个完整的全新副本,NSArray其中包含它们的新内存引用。NSJSONSerializationNSArray/NSDictionary

但要确保 NSArray/NSDictionary 的对象及其子对象必须是 JSON 可序列化的。

NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];
于 2015-07-24T08:49:54.047 回答