注:如果没有特别指出,这里的集合均指:collection。

对象的复制有两种:浅拷贝和深拷贝。正常的拷贝是浅拷贝,产生一个新的集合,共享原对象的所有权。深拷贝是从原对象创建新的对象,并把这些新对象添加到新的集合。这种差异如图1所示。

图 1. 浅拷贝和深拷贝

浅拷贝

有多种方法来创建一个集合(a collection)的浅拷贝。当你创建一个浅拷贝,便给原来集合的对象发送一个 retain 消息,对象的指针被复制到新的集合。清单1显示了一些使用浅拷贝来创建新的集合的方法。

清单 1  创建浅拷贝

NSArray *shallowCopyArray=[someArray copyWithZone:nil]; NSDictionary *shallowCopyDict=[[NSDictionary alloc] initWithDictionary: someDictionary copyItems: NO];

这些技巧不局限于上述集合类。比如,您可以使用 copyWithZone: 方法拷贝一个集合(a set),或者使用 mutableCopyWithZone:,也可以使用 initWithArray:copyItems: 方法来拷贝数组。

深拷贝

有两种方法来创建一个集合的深拷贝。您可以使用集合的等价方法 initWithArray:copyItems:,不过第2个参数为:YES。如果您使用这种方法创建了一个集合的深拷贝,集合中的每个对象都被发送一个 copyWithZone: 消息。如果集合中的对象都实现 NSCopying 协议,对象被深拷贝到新的集合,这个集合是新复制的对象的唯一所有者。如果对象没有实现 NSCopying 协议,那么试图用这种方法复制它们会导致一个运行时错误结果。然而,copyWithZone: 产生一个浅拷贝。这种复制方法仅产生一个一级深度的拷贝。如果你只需要一级深度的拷贝,您可以使用清单2的方法。

清单 2  创建深拷贝

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

这个技巧也适用于其他集合。使用集合的等价方法 initWithArray:copyItems:,第2个参数为:YES

如果您需要一个真正的深拷贝,比如对于一个数组,可以使用archive和unchive来处理,数组元素要符合 NSCoding 协议。您可以使用清单3的方法。

清单 3  真正的深拷贝

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

举例说明

    NSArray *n = [[NSArray alloc] initWithObjects:[NSMutableString stringWithString:@"123"],@"456", nil];

    // = [n copy]
    // The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object;
    // otherwise the exact nature of the copy is determined by the class.
    NSMutableArray *n1 = [n copyWithZone:nil];

    // = [n mutableCopy]
    // The copy returned is mutable whether the original is mutable or not.
    NSMutableArray *n2  = [n mutableCopyWithZone:nil];

    NSArray *n3 = [[NSArray alloc] initWithArray:n copyItems:NO];

    NSArray *n4 = [[NSArray alloc] initWithArray:n copyItems:YES];

    NSArray *n5 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: n]];

    NSLog(@"Array n = %@", n);
    NSLog(@"Array n1 = %@", n1);
    NSLog(@"Array n2 = %@", n2);
    NSLog(@"Array n3 = %@", n3);
    NSLog(@"Array n4 = %@", n4);
    NSLog(@"Array n5 = %@", n5);

    NSMutableString *nstr1 = [n objectAtIndex:0];
    [nstr1 appendString:@"abc"];

    [n2 addObject:@"xxx"];

    NSLog(@"Array n = %@", n);
    NSLog(@"Array n1 = %@", n1);
    NSLog(@"Array n2 = %@", n2);
    NSLog(@"Array n3 = %@", n3);
    NSLog(@"Array n4 = %@", n4);
    NSLog(@"Array n5 = %@", n5); 

如图,左图是更改值之前的地址信息,右图是更改后的地址信息。

对第1种深拷贝方法来说,我们发现不可变元素,还是进行了指针复制(反正是不可变的,^_^)。

第2种深拷贝才是真正的深拷贝,全新的内存地址。