iOS深复制与浅复制
一、深复制与浅复制
概念
对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中。
再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝。
集合的浅复制
集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。
现在让我们看一些浅复制的例子:
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSSet *shallowCopySet = [NSSet mutableCopyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];
集合的深复制
集合的深复制有两种方法。可以用initWithArray:copyItems:
将第二个参数设置为YES
即可深复制,如:
NSArray *shallowCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];
如果你用这种方法深复制,集合里的每个对象都会收到copyWithZone:
消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone:
这种拷贝方式只能够提供一层内存拷贝,而非真正的深复制。
第二个方法是将集合进行归档(archive),然后解档(unarchive),如:
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
二、copy与mutableCopy
不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:
- copy返回unmutable对象;所以,如果对copy返回值使用mutable对象接收就会crash;
- mutableCopy返回mutable对象;
下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述
非集合类对象的copy与mutableCopy
系统非集合类对象指的是 NSString, NSNumber ... 之类的对象。
下面先看个非集合类unmutable对象拷贝的例子
NSString *string = @"origin";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
打印内存地址
string = 0x1000eda78
stringCopy = 0x1000eda78
stringMCopy = 0x17407e8c0
可以看到stringCopy
和string
的地址是一样,说明进行了指针拷贝;而stringMCopy
的地址和string
不一样,说明进行了内容拷贝;
再看mutable对象拷贝例子
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对象,删掉该行,再次运行,打印内存地址
mString = 0x174266940
stringCopy = 0x1742294a0
mStringCopy = 0x1742294c0
mStringMCopy = 0x174266980
会发现四个对象的内存地址都不一样,说明此时都是做内容拷贝。
综上两个例子,我们可以得出结论:
在非集合类对象中:
对unmutable对象进行copy操作是指针复制,mutableCopy操作是内容复制;
对mutable对象进行copy和mutableCopy都是内容复制。
用代码简单表示如下:
[unmutableObject copy] // 浅复制
[unmutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制
集合类对象的copy与mutableCopy
集合类对象是指NSArray、NSDictionary、NSSet ... 之类的对象。
下面先看集合类unmutable对象使用copy和mutableCopy的一个例子:
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
打印内存地址
array = 0x1700353e0
arrayCopy = 0x1700353e0
arrayMCopy = 0x17024dad0
可以看到arrayCopy
和array
的地址是一样的,而arrayMCopy
和array
的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array
这个对象,array
集合内部的元素仍然是指针拷贝。
再看mutable对象拷贝的例子:
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:@[@"a", @"b", @"c"]];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
打印内存地址
array = 0x17405eed0
arrayCopy = 0x17405eea0
arrayMCopy = 0x17405ef00
如我们所料,arrayCopy
、arrayMCopy
和array
的内存地址都不一样,说明arrayCopy
、arrayMCopy
都对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 集合的深复制与浅复制