18.2 浅复制与深复制
代码清单18-1使用不可变字符串来填充dataArray的元素(回忆一下,常量字符串对象是不可变的)。在代码清单18-2中,将使用可变字符串代替它来填充数组,这样就可以改变数组中的一个字符串。观察代码清单18-2,看自己能否理解程序的输出结果。
代码清单18-2
import<Foundation/NSObject.h>
import<Foundation/NSArray.h>
import<Foundation/NSString.h>
import<Foundation/NSAutoreleasePool.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSMutableArray*dataArray=[NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:@“one”],
[NSMutableString stringWithString:@“two”],
[NSMutableString stringWithString:@“three”],
nil
];
NSMutableArray*dataArray2;
NSMutableString*mStr;
NSLog(@“dataArray:);
for(NSString*elem in dataArray)
NSLog(@,elem);
//make a copy, then change one of the strings
dataArray2=[dataArray mutableCopy];
mStr=[dataArray objectAtIndex:0];
[mStr appendString:@“ONE”];
NSLog(@“dataArray:);
for(NSString*elem in dataArray)
NSLog(@,elem);
NSLog(@“dataArray2:);
for(NSString*elem in dataArray2)
NSLog(@,elem);
[dataArray2 release];
[pool drain];
return 0;
}
代码清单18-2输出
dataArray:
one
two
three
dataArray:
oneONE
two
three
dataArray:
oneONE
two
three
使用下面的语句检索dataArray的第一个元素:
mStr=[dataArray objectAtIndex:0];
然后,使用下面的语句将字符串@“ONE”附加到给这个元素:
[mStr appendString:@“ONE”];
注意,原始数组及其副本中第一个元素的值:它们都被修改了。或许你能理解为什么dataArray的第一个元素发生改变,但不明白为什么它的副本也会改变。从集合中获取元素时,就得到了这个元素的一个新引用,但并不是一个新副本。所以,对dataArray调用objectAtIndex:方法时,返回的对象与dataArray中的第一个元素都指向内存中的同一个对象。随后,修改string对象mStr的副作用就是同时改变了dataArray的第一个元素,从输出结果中可以看到。
但你制作的副本怎么样了?为什么它的第一个元素也会改变?这与默认的浅复制方式有关。它意味着使用mutableCopy方法复制数组时,在内存中为新的数组对象分配了空间,并且将单个元素复制到新数组中。然而将原始数组中每个元素复制到新位置意味着:仅将引用从一个数组元素复制到另一个数组元素。这样做的最终结果,就是这两个数组中的元素都指向内存中的同一个字符串。这与将一个对象赋值给另一个对象没什么不同,就像我们本章开始提到的一样。
要为数组中每个元素创建完全不同的副本,需要执行所谓的深复制。这就意味着要创建数组中的每个对象内容的副本,而不仅是这些对象的引用的副本(并且考虑一下,如果一个数组中的元素本身是数组对象时,深复制意味着该如何处理)。然而使用Foundation类的copy或mutableCopy方法时,深复制并不是默认执行的。在第19章“归档”中,我们将向你展示如何使用Foundation的归档功能来创建对象的深复制。
例如,复制一个数组、字典或集时,会获得这些集合的新副本。然而,如果想要更改其中一个集合而不是它的副本,那么可能需要为单个元素创建自己的副本。例如,假设想要更改代码清单18-2中dataArray2的第一个元素,但不更改dataArray的第一个元素,可以创建一个新字符串(使用stringWithString:之类的方法)并将它存储到dataArray2的第一个位置,如下所示:
mStr=[NSMutableString stringWithString:[dataArray2 objectAtIndex:0]];
然后,可以更改mStr,并使用replaceObject:atIndex:withObject:方法将它添加到数组中,如下所示:</p>
[mStr appendString@“ONE”];
[dataArray2 replaceObjectAtIndex:0 withObject:mStr];
如果顺利的话,你会发现即使替换了数组中的对象之后,mStr和dataArray2的第一个元素仍指向内存中的同一个对象。这意味着随后在程序中对mStr做的修改也将更改数组的第一个元素。如果这不是你想要的,则可以总是释放m S t r,并分配新实例,因为对象会被replaceObject:atIndex:withObject:方法自动保持。