Deep copying an NSArray

是否有任何内置的功能,让我深入复制一个 NSMutableArray

I looked around, some people say [aMutableArray copyWithZone:nil] works as deep copy. But I tried and it seems to be a shallow copy.

现在我用 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];
}
}
}
}

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

82779 次浏览

我所知道的唯一容易做到这一点的方法是存档,然后立即取消存档您的数组。这看起来有点像黑客,但实际上是在 关于复制收藏品的苹果文档中明确建议的,它指出:

如果您需要一个真正的深度副本,比如当您拥有一个数组数组时,您可以对集合进行存档,然后取消存档,前提是所有内容都符合 NSCoding 协议。这种技术的一个示例如清单3所示。

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

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

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

Swift 2版本:

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

正如 关于深度拷贝的苹果文档明确指出的:

如果你只需要一份 一级深度副本:

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

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

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

默认情况下,复制将提供浅表复制

这是因为调用 copycopyWithZone:NULL相同,也称为使用默认区域进行复制。copy调用不会导致深度副本。在大多数情况下,它会给您一个浅副本,但是在任何情况下,它都取决于类。对于一个彻底的讨论,我推荐在 Apple Developer 站点上使用 集合编程主题

initWithArray:CopyItems: gives a one-level deep copy

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

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

对于一个真正的深度拷贝(Array of Array) ,你需要 NSCoding并存档/取消存档对象:

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

No, there isn't something built into the frameworks for this. Cocoa collections support shallow copies (with the copy or the arrayWithArray: methods) but don't even talk about a deep copy concept.

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

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

@ AndrewGrant 的回答建议使用键控存档/非存档是一种不可执行但正确且干净的方法,可以对任意对象实现这一点。This book even goes so far so suggest为所有完全支持深度复制的对象添加一个类别。

如果试图实现 JSON 兼容数据的深度复制,我有一个解决方案。

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

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

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

字典

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

数组

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