深拷贝一个NSArray

有没有内置的函数,允许我深入复制一个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]; } } } } 

但我想要一个更简洁,更简洁的解决scheme。

正如苹果公司的文件所述 ,

如果您只需要一个一级深度的副本,您可以明确地要求一个…

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

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

请注意,如果您需要深入复制整个嵌套数据结构(链接的Apple文档称为“真正的深层副本”) ,那么这种方法是不够的 – 请参阅此处的其他答案。

我所知道的唯一方法就是将其存档,然后立即取消存档您的数组。 这感觉就像是一个黑客,但实际上在苹果文档中明确build议复制集合 ,其中指出:

如果您需要一个真正的深层副本,例如当您有一个数组数组时,您可以将该集合归档,然后取消归档,前提是内容全部符合NSCoding协议。 清单3给出了这种技术的一个例子。

清单3一个真正的深层副本

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

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

Swift 2版本:

 let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData( NSKeyedArchiver.archivedDataWithRootObject(oldArray)) 

默认情况下复制给出浅拷贝

这是因为调用copycopyWithZone:NULL相同copyWithZone:NULL也称为使用默认区域进行复制。 copy调用不会导致深层复制。 在大多数情况下,它会给你一个浅的副本,但在任何情况下,这取决于类。 为了进行彻底的讨论,我推荐Apple开发者网站上的Collections Programming Topics 。

initWithArray:CopyItems:给出一个一级深度的副本

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

NSCoding是苹果推荐的提供深层拷贝的方式

对于一个真正的深层复制(Array of Arrays),你将需要NSCoding和存档/取消存档对象:

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

对于Dictonary

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

对于数组

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

不,没有为框架内置的东西。 cocoa收集支持浅拷贝(与copyarrayWithArray:方法),但甚至不谈论深层复制的概念。

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

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

@ AndrewGrant的答案显示,使用密钥存档/取消存档是一种非常规的方法,但对于任意对象来说,这是一种正确且干净的方式。 这本书甚至到目前为止,所以build议添加一个类别的所有对象,完全是为了支持深度复制。

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

只需使用NSJSONSerialization获取NSArray NSData ,然后重新创buildJSON对象,这将创build一个NSArray/NSDictionary的新的全新副本,并带有新的内存引用。

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

 NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil]; NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];