iOS深复制与浅复制

一、深复制与浅复制

1.概念

对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中。
再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝。

2.集合的浅复制

集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。
现在让我们看一些浅复制的例子:

1
2
3
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSSet *shallowCopySet = [NSSet mutableCopyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

3.集合的深复制

集合的深复制有两种方法。可以用initWithArray:copyItems:将第二个参数设置为YES即可深复制,如:

1
2
NSArray *shallowCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

如果你用这种方法深复制,集合里的每个对象都会收到copyWithZone:消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone:这种拷贝方式只能够提供一层内存拷贝,而非真正的深复制。

第二个方法是将集合进行归档(archive),然后解档(unarchive),如:

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

二、copy与mutableCopy

不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:

  • copy返回unmutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;
  • mutableCopy返回mutable对象;

下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述

1.非集合类对象的copy与mutableCopy

系统非集合类对象指的是 NSString, NSNumber … 之类的对象。

下面先看个非集合类unmutable对象拷贝的例子

1
2
3
NSString *string = @"origin";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];

打印内存地址

1
2
3
string = 0x1000eda78
stringCopy = 0x1000eda78
stringMCopy = 0x17407e8c0

可以看到stringCopystring的地址是一样,说明进行了指针拷贝;而stringMCopy的地址和string不一样,说明进行了内容拷贝;

再看mutable对象拷贝例子

1
2
3
4
5
NSMutableString *mString = [NSMutableString stringWithString:@"origin"];
NSString *stringCopy = [mString copy];
NSMutableString *mStringCopy = [mString copy];
NSMutableString *mStringMCopy = [mString mutableCopy];
[mStringCopy appendString:@"mm"];

运行上述代码会在最后一行[mStringCopy appendString:@"mm"];处crash,原因是copy返回的对象是unmutable对象,删掉该行,再次运行,打印内存地址

1
2
3
4
mString = 0x174266940
stringCopy = 0x1742294a0
mStringCopy = 0x1742294c0
mStringMCopy = 0x174266980

会发现四个对象的内存地址都不一样,说明此时都是做内容拷贝。

综上两个例子,我们可以得出结论:

在非集合类对象中:
对unmutable对象进行copy操作是指针复制,mutableCopy操作是内容复制;
对mutable对象进行copy和mutableCopy都是内容复制。
用代码简单表示如下:
[unmutableObject copy] // 浅复制
[unmutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制

2.集合类对象的copy与mutableCopy

集合类对象是指NSArray、NSDictionary、NSSet … 之类的对象。

下面先看集合类unmutable对象使用copy和mutableCopy的一个例子:

1
2
3
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];

打印内存地址

1
2
3
array = 0x1700353e0
arrayCopy = 0x1700353e0
arrayMCopy = 0x17024dad0

可以看到arrayCopyarray的地址是一样的,而arrayMCopyarray的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。

再看mutable对象拷贝的例子:

1
2
3
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:@[@"a", @"b", @"c"]];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];

打印内存地址

1
2
3
array = 0x17405eed0
arrayCopy = 0x17405eea0
arrayMCopy = 0x17405ef00

如我们所料,arrayCopyarrayMCopyarray的内存地址都不一样,说明arrayCopyarrayMCopy都对array进行了内容拷贝。同样,我们可以得出结论:

在集合类对象中:
对unmutable对象进行copy是指针复制,mutableCopy是内容复制;
对mutable对象进行copy和mutableCopy都是内容复制。
但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。
用代码简单表示如下:
[unmutableObject copy] // 浅复制
[unmutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制


再解释一下【集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制】是什么意思呢?
举个例子,也是我们开发中使用MVC经常遇到的,现在有一个mutableArray数组里面存放了若干Model,我想再创建一个新的mutableArray1,并将mutableArray赋值给mutableArray1,然后对mutableArray1中的Model进行改变但又不影响mutableArray。

此时如果用copy或mutableCopy复制是不行的,正如所说的集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制,对象元素也就是数组里面的Model是指针复制,所以改变的话同样会影响mutableArray。
那该怎么办呢?请移步到上面👆集合的深复制,有两种方法可以解决该问题。


参考链接
iOS 集合的深复制与浅复制

坚持原创技术分享,您的支持将鼓励我继续创作!
0%