深拷贝和浅拷贝

16

我已经阅读了问题“深拷贝和浅拷贝有什么区别?”的答案。现在我有一个疑问,当我们通过以下方式制作浅层副本时:

 newArray = [NSmutableArray arrayWithArray:oldArray];
新数组将指向旧数组 (如图所示)。现在,当我从新数组中删除对象时会发生什么?从图中可以看出,它应该同时从旧数组中删除相同的元素!!! 看起来像是 newArray = oldArray 是浅拷贝,而 newArray = [NSmutableArray arrayWithArray:oldArray]; 是深拷贝。这是正确的吗?

2
新数组 = 旧数组根本不是副本。它只是指向同一数组的第二个变量。 - Ben Clayton
使用 arrayWithArray:,您可以获得一个新的数组,该数组引用相同的对象。 - user971401
这是一种复制数组的深拷贝方式。https://dev59.com/r3RB5IYBdhLWcg3wXWK2#647269 - Ali Amin
我找到了这个很好的答案,可以复制真正的深度拷贝 https://dev59.com/r3RB5IYBdhLWcg3wXWK2#15040786 - Ali Amin
4个回答

43

newArray = oldArray 并不是真正的复制。你最终会得到两个指向完全相同内存位置的指针。

newArray = [NSMutableArray arrayWithArray:oldArray]; 是浅拷贝。你最终会得到两个独立的数组,因此如果你从一个数组中删除或添加项,它不会影响另一个数组。然而,这两个数组中的是相同的。如果oldArray的第一个元素是NSMutableDictionary,并且你向其中添加了一个键,那么你也会在newArray的第一个元素上看到该更改(因为这两个对象是相同的)。

要进行深层复制,你需要创建一个新数组,并将新数组的每个元素都深度复制对应的旧数组元素。(是的,这个定义是递归的)。


1
根据苹果公司的说法(https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/ObjectCopying.html),如果将对象A浅复制到对象B,则对象B引用与对象A相同的实例变量(或属性)。因此,newArray = [NSMutableArray arrayWithArray:oldArray]; 是深拷贝而不是浅拷贝。如果我有错误,请纠正我。 - Harshit Gupta

11

首先,NSArray没有深拷贝函数。不过,你可以通过以下方式创建一个深拷贝函数:

@interface NSArray(deepCopy)

-(NSArray *) deepCopy;

@end

@implementation

-(NSArray *) deepCopy
{
    NSMutableArray *ret = [NSMutableArray array];

    for (id val in self)
    {
        if ([val conformsToProtocol:@protocol(NSCopying)])
        { 
            [ret addObject:[val copy]];
        }
        else
        {
           [ret addObject:val];
        }
    }

    return ret;
}

@end 

其次,newArray = oldArray不会复制数组。它只是让newArray指向oldArray所指向的数组。

第三,+arrayWithArray:对数组进行浅拷贝,这意味着单个对象不会被复制


我认为你忘记了在 [ret addObject:val]; 周围加上 else。 - Matthias Bauch
我认为[ret addObject:[val copy]]是一个内存泄漏,假设根据苹果文档采用了NSCopying协议(copyWithZone:返回的对象被发送者隐式保留)。 - SundayMonday
1
在这种情况下,我会假设使用了自动引用计数(ARC)。 - trapper
1
[ret addObject:[val copy]];将浅复制原始数组中的每个元素。如果我有一个使用您的方法(deepcopy)的数组数组,那么最终我将得到一个新的数组,其中内部数组是浅复制的。这是错误的,不是吗? - Ali Amin
@عليامين 不,这不是“错误”,而是一个边缘情况。如果自定义对象的复制实现没有进行深度复制(像它应该做的那样),除了序列化和反序列化之外,我几乎无能为力。 - Richard J. Ross III

2

你也可以调用[[NSArray alloc] initWithArray:arraytoBeCopied copyItems:YES];


-8
在Objective-C中,“Copy”关键字只会增加对象的“保留计数”,因此仅使用“Copy”不会执行复制操作。
但是,当我们对对象进行更改时,Objective-C会在那时创建原始对象的副本。
如果我有错误,请纠正我。
谢谢。

1
我不认为你的答案是正确的。事实上,我相当确定它不是正确的。 - RonLugge
你可以在苹果文档中找到它。谢谢。 - iPhoneBuddy
1
真的吗?因为当我查看文档时,我发现NSObject说复制是copyWithZone:的快捷方式,该方法必须由希望按照NSCopying协议启用该方法的子类进行实现。该协议指定CopyWithZone创建具有相同属性的新对象-而不是增加保留计数。此外,自从ARC被添加以来,苹果已明确禁用了所有保留计数的使用。因此,无论怎样解释,“复制”只会增加对象的保留计数都是错误的。 - RonLugge
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/Collections/Articles/Copying.html#//apple_ref/doc/uid/TP40010162-SW3 - iPhoneBuddy
4
抱歉我没有更清楚地表述我的立场:我并不是在与你的第二段争论。就我所知,那是正确的(尽管你链接的文档指出它并不能产生“真正”的深层复制,因为它只会向下复制一层——在我看来,这是一个缺陷)。我非常具体地与你的第一段争论。实际上,“复制”确实会返回该类的一个新实例。只是该实例的内容是旧对象的引用,而不是副本。 - RonLugge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接