简述深浅拷贝
我们实例化的对象存储在堆区,而指向对象的指针一般存储在栈区。我们需要知道这个前提。
实际上拷贝分为深拷贝(one level deep copy),浅拷贝(shallow copy)和完全拷贝(real deep copy)三种。
- 浅拷贝:在操作中,对于被复制对象的每一层都是指针复制。
- 深拷贝:在操作中,对于被复制对象,至少有一层是深复制。
- 完全拷贝:在操作中,对于被复制对象,每一层都是对象复制。
通过下图我们来看深浅拷贝。
通过上面两个图我们可以这么认为,浅拷贝就是指针层面的赋值,指针1复制为指针2,它们指向了同一个对象;深拷贝是指针和对象的全拷贝,指针1拷贝为指针2,对象1拷贝为对象2,对象1和对象2是独立的占用不同的地址。下面我们还可以看这张我在官方文档上借用的图,也可以说明我上面写的问题。
下面我们对深拷贝、浅拷贝和完全拷贝进行分析归纳和测试。
详述深浅拷贝
在深入研究深浅拷贝之前,我们需要载体,这里我们以非可变对象(NSArray、NSString、NSDictionary)和可变对象(NSMutableArray、NSMutableString、NSMutableDictionary)以及自定义对象进行研究测试。分别研究它们的copy和mutableCopy(这里假设阅读者已经知道copy和mutableCopy了,不再赘述),并研究它们的引用计数。
一、NSString 和 NSMutableString的拷贝
● ARC下的拷贝
1. NSString的copy和mutableCopy
不多说别的,直接上代码。
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = @"虫儿不会飞";
NSString *strCopy = [str copy];
NSMutableString *strMutableCopy = [str mutableCopy];
NSLog(@"str---%@---%p----%@----",str,str,[str class]);
NSLog(@"strCopy---%@---%p----%@----",strCopy,strCopy,[strCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strMutableCopy,strMutableCopy,[strMutableCopy class]);
}
看打印输出结果
2017-03-11 14:07:47.692 test111[5122:374020] str---虫儿不会飞---0x10b8fe078----__NSCFConstantString----
2017-03-11 14:07:47.692 test111[5122:374020] strCopy---虫儿不会飞---0x10b8fe078----__NSCFConstantString----
2017-03-11 14:07:47.693 test111[5122:374020] strMutableCopy---虫儿不会飞---0x60800026b540----__NSCFString----
结论:不可变字符串NSString,它的copy出来的对象地址和原对象一样是浅拷贝,而mutableCopy后的对象地址和原对象地址不一样,是深拷贝。
2. NSMutableString的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str =[NSMutableString stringWithString:@"虫儿不会飞"];
NSString *strCopy = [str copy];
NSMutableString *strMutableCopy = [str mutableCopy];
NSLog(@"str---%@---%p----%@----",str,str,[str class]);
NSLog(@"strCopy---%@---%p----%@----",strCopy,strCopy,[strCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strMutableCopy,strMutableCopy,[strMutableCopy class]);
}
看打印输出结果
2017-03-11 23:34:22.591 test111[6244:450630] str---虫儿不会飞---0x6000000783c0----__NSCFString----
2017-03-11 23:34:22.591 test111[6244:450630] strCopy---虫儿不会飞---0x60000005d370----__NSCFString----
2017-03-11 23:34:22.591 test111[6244:450630] strMutableCopy---虫儿不会飞---0x6000000786c0----__NSCFString----
结论:变字符串NSMutableString,它的copy和mutableCopy出来的对象地址和原对象地址都不是一样的,是深拷贝。
● MRC下的拷贝
在开始测试之前,我们需要先将工程设置为MRC模式,设置方法不需多说,基本都会了吧,我就直接上图了。
1. NSString的copy和mutableCopy
// MRC下NSString 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSString * str = @"虫儿不会飞";
NSLog(@"str--%@--%p--%lu--%@",str,str,[str retainCount],[str class]);
NSString *strCopy = [str copy];
[strCopy retain];
NSLog(@"strCopy--%@--%p--%lu--%@",strCopy,strCopy,[strCopy retainCount],[strCopy class]);
NSMutableString *strMCopy = [str mutableCopy];
[strMCopy retain];
NSLog(@"strMCopy--%@--%p--%lu--%@",strMCopy,strMCopy,[strMCopy retainCount],[strMCopy class]);
}
看结果
2017-03-13 21:22:12.676 MRC拷贝研究[2609:156555] str--虫儿不会飞--0x106a8e050--18446744073709551615--__NSCFConstantString
2017-03-13 21:22:12.676 MRC拷贝研究[2609:156555] strCopy--虫儿不会飞--0x106a8e050--18446744073709551615--__NSCFConstantString
2017-03-13 21:22:12.677 MRC拷贝研究[2609:156555] strMCopy--虫儿不会飞--0x608000070a80--2--__NSCFString
结论:不可变字符串NSString,它的原对象和拷贝对象地址相同copy是浅拷贝,mutableCopy是深拷贝;引用计数,copy以后retainCount近似无穷大的数,所以不用管理它的释放,mutableCopy后的对象计数为1,retain在加1,最后为2。
2. NSMutableString的copy和mutableCopy
// MRC下NSMutableString 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSString * strI = @"虫儿不会飞";
NSMutableString * str = [NSMutableString stringWithString:strI];
NSLog(@"str--%@--%p--%lu--%@",str,str,[str retainCount],[str class]);
NSString *strCopy = [str copy];
[strCopy retain];
NSLog(@"strCopy--%@--%p--%lu--%@",strCopy,strCopy,[strCopy retainCount],[strCopy class]);
NSMutableString *strMCopy = [str mutableCopy];
[strMCopy retain];
NSLog(@"strMCopy--%@--%p--%lu--%@",strMCopy,strMCopy,[strMCopy retainCount],[strMCopy class]);
}
看结果
2017-03-13 21:36:25.763 MRC拷贝研究[2831:176161] str--虫儿不会飞--0x600000070000--1--__NSCFString
2017-03-13 21:36:25.763 MRC拷贝研究[2831:176161] strCopy--虫儿不会飞--0x608000242c70--2--__NSCFString
2017-03-13 21:36:25.764 MRC拷贝研究[2831:176161] strMCopy--虫儿不会飞--0x60000006f740--2--__NSCFString
结论:可变字符串NSMutableString,它的copy和mutableCopy均是深拷贝;引用计数,copy和mutableCopy后引用计数为1,retain在加1,最后为2。
二、NSArray 和 NSMutableArray的拷贝
● ARC下的拷贝
1. NSArray的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *strArr = @[@"111", @"222"];
NSArray *strArrCopy = [strArr copy];
NSMutableArray *strArrMutableCopy = [strArr mutableCopy];
NSLog(@"str---%@---%p----%@----",strArr,strArr,[strArr class]);
NSLog(@"strCopy---%@---%p----%@----",strArrCopy,strArrCopy,[strArrCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strArrMutableCopy,strArrMutableCopy,[strArrMutableCopy class]);
}
直接看结果
2017-03-11 23:46:17.145 test111[6436:467460] str---(
111,
222
)---0x60800002ba80----__NSArrayI----
2017-03-11 23:46:17.147 test111[6436:467460] strCopy---(
111,
222
)---0x60800002ba80----__NSArrayI----
2017-03-11 23:46:17.147 test111[6436:467460] strMutableCopy---(
111,
222
)---0x6080002441d0----__NSArrayM----
结论:不可变数组NSArray,它的copy所得对象地址和原对象地址相同,是浅拷贝。而mutableCopy后的对象地址和原对象地址不一样,是深拷贝。
2. NSMutableArray的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *strArr1 = @[@"111", @"222"];
NSMutableArray *strArrM = [NSMutableArray arrayWithArray:strArr1];
NSArray *strArrMCopy = [strArrM copy];
NSMutableArray *strArrMMutableCopy = [strArrM mutableCopy];
NSLog(@"str---%@---%p----%@----",strArrM,strArrM,[strArrM class]);
NSLog(@"strCopy---%@---%p----%@----",strArrMCopy,strArrMCopy,[strArrMCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strArrMMutableCopy,strArrMMutableCopy,[strArrMMutableCopy class]);
}
直接查看结果
2017-03-12 00:08:00.172 test111[6741:494893] str---(
111,
222
)---0x600000056b90----__NSArrayM----
2017-03-12 00:08:00.173 test111[6741:494893] strCopy---(
111,
222
)---0x600000039f00----__NSArrayI----
2017-03-12 00:08:00.173 test111[6741:494893] strMutableCopy---(
111,
222
)---0x600000056b30----__NSArrayM----
结论:可变数组NSMutableArray,它的copy和mutableCopy所得对象地址和原对象地址都不相同,是深拷贝。
● MRC下的拷贝
1. NSArray的copy和mutableCopy
// MRC下NSArray 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSArray * arrI = @[@"lalalalala"];
NSLog(@"arrI--%@--%p--%lu--%@",arrI,arrI,[arrI retainCount],[arrI class]);
NSArray *arrICopy = [arrI copy];
[arrICopy retain];
NSLog(@"arrICopy--%@--%p--%lu--%@",arrICopy,arrICopy,[arrICopy retainCount],[arrICopy class]);
NSMutableArray *arrMCopy = [arrI mutableCopy];
[arrMCopy retain];
NSLog(@"arrMCopy--%@--%p--%lu--%@",arrMCopy,arrMCopy,[arrMCopy retainCount],[arrMCopy class]);
}
看结果
2017-03-13 21:45:34.180 MRC拷贝研究[2999:188861] arrI--(
lalalalala
)--0x608000003510--1--__NSSingleObjectArrayI
2017-03-13 21:45:34.181 MRC拷贝研究[2999:188861] arrICopy--(
lalalalala
)--0x608000003510--3--__NSSingleObjectArrayI
2017-03-13 21:45:34.182 MRC拷贝研究[2999:188861] arrMCopy--(
lalalalala
)--0x608000048be0--2--__NSArrayM
结论:NSArray copy后为浅拷贝,mutableCopy后为深拷贝。但是需要注意,浅拷贝对象计数在原有基础上+1 ,为2,retain后再+1,为3。
2. NSMutableArray的copy和mutableCopy
// MRC下NSMutableArray 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSArray * arrI = @[@"lalalalala"];
NSMutableArray *arrM = [NSMutableArray arrayWithArray:arrI];
NSLog(@"arrI--%@--%p--%lu--%@",arrM,arrM,[arrM retainCount],[arrM class]);
NSArray *arrCopy = [arrM copy];
[arrCopy retain];
NSLog(@"arrICopy--%@--%p--%lu--%@",arrCopy,arrCopy,[arrCopy retainCount],[arrCopy class]);
NSMutableArray *arrMCopy = [arrM mutableCopy];
[arrMCopy retain];
NSLog(@"arrMCopy--%@--%p--%lu--%@",arrMCopy,arrMCopy,[arrMCopy retainCount],[arrMCopy class]);
}
看结果
2017-03-13 22:10:52.515 MRC拷贝研究[3394:225252] arrI--(
lalalalala
)--0x600000051940--1--__NSArrayM
2017-03-13 22:10:52.515 MRC拷贝研究[3394:225252] arrICopy--(
lalalalala
)--0x60000000d030--2--__NSSingleObjectArrayI
2017-03-13 22:10:52.516 MRC拷贝研究[3394:225252] arrMCopy--(
lalalalala
)--0x600000051850--2--__NSArrayM
结论:可变数组NSMutableArray的copy 和 mutableCopy均为深拷贝,copy 和 mutableCopy后对象的计数为1,retain+1,为2。
三、NSDictionary 和 NSMutableDictionary的拷贝
● ARC下的拷贝
1. NSDictionary的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *strDict = @{@"111":@"222"};
NSDictionary *strDictCopy = [strDict copy];
NSMutableDictionary *strDictMutableCopy = [strDict mutableCopy];
NSLog(@"str---%@---%p----%@----",strDict,strDict,[strDict class]);
NSLog(@"strCopy---%@---%p----%@----",strDictCopy,strDictCopy,[strDictCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strDictMutableCopy,strDictMutableCopy,[strDictMutableCopy class]);
}
直接看结果
2017-03-12 08:55:19.709 test111[1138:43171] str---{
111 = 222;
}---0x608000037b00----__NSSingleEntryDictionaryI----
2017-03-12 08:55:19.710 test111[1138:43171] strCopy---{
111 = 222;
}---0x608000037b00----__NSSingleEntryDictionaryI----
2017-03-12 08:55:19.710 test111[1138:43171] strMutableCopy---{
111 = 222;
}---0x608000059740----__NSDictionaryM----
结论:不可变字典NSDictionary,它的copy所得对象地址和原对象地址相同,是浅拷贝。而mutableCopy后的对象地址和原对象地址不一样,是深拷贝。
2. NSMutableDictionary的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *strDict = @{@"111":@"222"};
NSMutableDictionary *strDictM = [NSMutableDictionary dictionaryWithDictionary:strDict];
NSDictionary *strDictMCopy = [strDictM copy];
NSMutableDictionary *strDictMutableCopy = [strDictM mutableCopy];
NSLog(@"str---%@---%p----%@----",strDictM,strDictM,[strDictM class]);
NSLog(@"strCopy---%@---%p----%@----",strDictMCopy,strDictMCopy,[strDictMCopy class]);
NSLog(@"strMutableCopy---%@---%p----%@----",strDictMutableCopy,strDictMutableCopy,[strDictMutableCopy class]);
}
直接看结果
2017-03-12 08:59:03.704 test111[1201:48465] str---{
111 = 222;
}---0x60800005e870----__NSDictionaryM----
2017-03-12 08:59:03.705 test111[1201:48465] strCopy---{
111 = 222;
}---0x608000073f80----__NSDictionaryI----
2017-03-12 08:59:03.705 test111[1201:48465] strMutableCopy---{
111 = 222;
}---0x60800005e600----__NSDictionaryM----
结论:可变字典NSMutableDictionary,它的copy和mutableCopy出来的对象地址和原对象地址都不是一样的,是深拷贝。
● MRC下的拷贝
1. NSDictionary的copy和mutableCopy
// MRC下NSDictionary 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary * dictI = @{@"lalalalala":@"heiheiheihei"};
NSLog(@"dictI--%@--%p--%lu--%@",dictI,dictI,[dictI retainCount],[dictI class]);
NSDictionary *dictICopy = [dictI copy];
[dictICopy retain];
NSLog(@"dictICopy--%@--%p--%lu--%@",dictICopy,dictICopy,[dictICopy retainCount],[dictICopy class]);
NSMutableDictionary *dictMCopy = [dictI mutableCopy];
[dictMCopy retain];
NSLog(@"dictMCopy--%@--%p--%lu--%@",dictMCopy,dictMCopy,[dictMCopy retainCount],[dictMCopy class]);
}
看结果
2017-03-13 22:22:26.809 MRC拷贝研究[3589:242671] dictI--{
lalalalala = heiheiheihei;
}--0x60000002ac00--1--__NSSingleEntryDictionaryI
2017-03-13 22:22:26.810 MRC拷贝研究[3589:242671] dictICopy--{
lalalalala = heiheiheihei;
}--0x60000002ac00--3--__NSSingleEntryDictionaryI
2017-03-13 22:22:26.810 MRC拷贝研究[3589:242671] dictMCopy--{
lalalalala = heiheiheihei;
}--0x60800004e5e0--2--__NSDictionaryM
结论:NSDictionary的copy为浅拷贝,mutableCopy为深拷贝,浅拷贝后再retain,最后dictICopy的引用计数为3。
2. NSMutableDictionary的copy和mutableCopy
// MRC下NSMutableDictionary 拷贝
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary * dict = @{@"lalalalala":@"heiheiheihei"};
NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithDictionary:dict];
NSLog(@"dictM--%@--%p--%lu--%@",dictM,dictM,[dictM retainCount],[dictM class]);
NSDictionary *dictCopy = [dictM copy];
[dictCopy retain];
NSLog(@"dictCopy--%@--%p--%lu--%@",dictCopy,dictCopy,[dictCopy retainCount],[dictCopy class]);
NSMutableDictionary *dictMCopy = [dictM mutableCopy];
[dictMCopy retain];
NSLog(@"dictMCopy--%@--%p--%lu--%@",dictMCopy,dictMCopy,[dictMCopy retainCount],[dictMCopy class]);
}
看结果
2017-03-13 22:36:01.475 MRC拷贝研究[3787:261783] dictM--{
lalalalala = heiheiheihei;
}--0x6000000529c0--1--__NSDictionaryM
2017-03-13 22:36:01.475 MRC拷贝研究[3787:261783] dictCopy--{
lalalalala = heiheiheihei;
}--0x608000267e80--2--__NSDictionaryI
2017-03-13 22:36:01.476 MRC拷贝研究[3787:261783] dictMCopy--{
lalalalala = heiheiheihei;
}--0x6000000529f0--2--__NSDictionaryM
结论:可变字典的copy和mutableCopy均为深拷贝,深拷贝后对象引用计数为1,retain后为2。
四、自定义对象的拷贝
● ARC下的拷贝
我们先自定义一个对象,先看这个工程框架,为了看着方便,工程名字我就用汉字了,不规范,大家不要学,这里只是为了好识别而已。
我们自定义了一个类 DDCity,下面我们看一下工程里的文件我都在里面写了什么。
DDCity.h文件
#import
@interface DDCity : NSObject
@property (nonatomic,copy) NSString * cityName;
@property (nonatomic,copy) NSString * cityLocation;
@end
ViewController.m文件
#import "ViewController.h"
#import "DDCity.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
DDCity *city = [[DDCity alloc] init];
city.cityName = @"北京";
city.cityLocation = @"中国";
DDCity *cityCopy = [city copy];
DDCity *cityMCopy = [city mutableCopy];
NSLog(@"city---%@---%@",city.cityName,city.cityLocation);
NSLog(@"cityCopy---%@---%@",cityCopy.cityName,cityCopy.cityLocation);
NSLog(@"cityMCopy---%@---%@",cityMCopy.cityName,cityMCopy.cityLocation);
NSLog(@"city---%@---%p---%@",city,city,[city class]);
NSLog(@"cityCopy---%@---%p---%@",cityCopy,cityCopy,[cityCopy class]);
NSLog(@"cityMCopy---%@---%p---%@",cityMCopy,cityMCopy,[cityMCopy class]);
}
运行看结果crash,看提示信息
2017-03-12 10:54:44.930 自定义对象copy[2428:129638] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[DDCity copyWithZone:]: unrecognized selector sent to instance 0x6000000276e0'
*** First throw call stack:
(
0 CoreFoundation 0x000000010d3ffd4b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010ce6121e objc_exception_throw + 48
2 CoreFoundation 0x000000010d46ff04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
从提示上我们看见是没有实现copyWithZone 方法,这里我们就在DDCity.m中实现对应的copyWithZone方法。
在DDCity.h文件中增加协议
@interface DDCity : NSObject
在DDCity.m中实现两个协议方法
- (instancetype) copyWithZone:(NSZone *)zone {
DDCity *city = [[DDCity allocWithZone:zone] init];
NSLog(@"没有我copyWithZone你自定义对象就不能copy");
return city;
}
- (instancetype) mutableCopyWithZone:(NSZone *)zone {
DDCity *city = [[DDCity allocWithZone:zone] init];
NSLog(@"没有我mutableCopyWithZone你自定义对象就不能MCopy");
return city;
}
viewController中的代码不动,再次运行看结果
2017-03-12 11:32:52.068 自定义对象copy[3039:180702] 没有我copyWithZone你自定义对象就不能copy
2017-03-12 11:32:52.068 自定义对象copy[3039:180702] 没有我mutableCopyWithZone你自定义对象就不能MCopy
2017-03-12 11:32:52.069 自定义对象copy[3039:180702] city---北京---中国
2017-03-12 11:32:52.069 自定义对象copy[3039:180702] cityCopy---(null)---(null)
2017-03-12 11:32:52.069 自定义对象copy[3039:180702] cityMCopy---(null)---(null)
2017-03-12 11:32:52.070 自定义对象copy[3039:180702] city------0x600000037020---DDCity
2017-03-12 11:32:52.070 自定义对象copy[3039:180702] cityCopy------0x600000037080---DDCity
2017-03-12 11:32:52.071 自定义对象copy[3039:180702] cityMCopy------0x6000000370a0---DDCity
结论:1)自定义对象copy和mutableCopy后的对象地址都不一样,均为深拷贝。2)拷贝后的对象属性cityName和cityLocation均为null,也就是说属性并未拷贝,我们再次改进DDCity.m中的代码。
- (instancetype) copyWithZone:(NSZone *)zone {
DDCity *city = [[DDCity allocWithZone:zone] init];
//新增下面两行代码
city.cityName = self.cityName;
city.cityLocation = self.cityLocation;
NSLog(@"没有我copyWithZone你自定义对象就不能copy");
return city;
}
- (instancetype) mutableCopyWithZone:(NSZone *)zone {
DDCity *city = [[DDCity allocWithZone:zone] init];
//新增下面两行代码
city.cityName = self.cityName;
city.cityLocation = self.cityLocation;
NSLog(@"没有我mutableCopyWithZone你自定义对象就不能MCopy");
return city;
}
查看结果
2017-03-12 11:49:20.900 自定义对象copy[3304:202108] 没有我copyWithZone你自定义对象就不能copy
2017-03-12 11:49:20.901 自定义对象copy[3304:202108] 没有我mutableCopyWithZone你自定义对象就不能MCopy
2017-03-12 11:49:20.902 自定义对象copy[3304:202108] city---北京---中国
2017-03-12 11:49:20.902 自定义对象copy[3304:202108] cityCopy---北京---中国
2017-03-12 11:49:20.902 自定义对象copy[3304:202108] cityMCopy---北京---中国
2017-03-12 11:49:20.903 自定义对象copy[3304:202108] city------0x608000036d60---DDCity
2017-03-12 11:49:20.903 自定义对象copy[3304:202108] cityCopy------0x608000036da0---DDCity
2017-03-12 11:49:20.904 自定义对象copy[3304:202108] cityMCopy------0x600000037560---DDCity
结论:通过增加对属性的赋值,新拷贝的对象就拥有了原对象的属性值。
● MRC下的拷贝
- (void)viewDidLoad {
[super viewDidLoad];
DDCity *city = [[DDCity alloc] init];
city.cityName = @"北京";
city.cityLocation = @"中国";
DDCity *cityCopy = [city copy];
[cityCopy retain];
DDCity *cityMCopy = [city mutableCopy];
[cityMCopy retain];
NSLog(@"city---%@---%@",city.cityName,city.cityLocation);
NSLog(@"cityCopy---%@---%@",cityCopy.cityName,cityCopy.cityLocation);
NSLog(@"cityMCopy---%@---%@",cityMCopy.cityName,cityMCopy.cityLocation);
NSLog(@"city---%@---%p---%@",city,city,[city class]);
NSLog(@"cityCopy---%@---%p---%@",cityCopy,cityCopy,[cityCopy class]);
NSLog(@"cityMCopy---%@---%p---%@",cityMCopy,cityMCopy,[cityMCopy class]);
NSLog(@"city--%lu---%lu---%lu",city.retainCount,cityCopy.retainCount,cityMCopy.retainCount);
NSLog(@"city--%lu---%lu---%lu",city.cityName.retainCount,cityCopy.cityName.retainCount,cityMCopy.cityName.retainCount);
NSLog(@"city--%lu---%lu---%lu",city.cityLocation.retainCount,cityCopy.cityLocation.retainCount,cityMCopy.cityLocation.retainCount);
}
查看结果
2017-03-13 23:08:28.179 自定义对象copy[4284:303058] 没有我copyWithZone你自定义对象就不能copy
2017-03-13 23:08:28.180 自定义对象copy[4284:303058] 没有我mutableCopyWithZone你自定义对象就不能MCopy
2017-03-13 23:08:28.180 自定义对象copy[4284:303058] city---北京---中国
2017-03-13 23:08:28.180 自定义对象copy[4284:303058] cityCopy---北京---中国
2017-03-13 23:08:28.181 自定义对象copy[4284:303058] cityMCopy---北京---中国
2017-03-13 23:08:28.181 自定义对象copy[4284:303058] city------0x60000002ab00---DDCity
2017-03-13 23:08:28.182 自定义对象copy[4284:303058] cityCopy------0x60000002ab60---DDCity
2017-03-13 23:08:28.182 自定义对象copy[4284:303058] cityMCopy------0x608000028ec0---DDCity
2017-03-13 23:08:28.182 自定义对象copy[4284:303058] city--1---2---2
2017-03-13 23:08:28.183 自定义对象copy[4284:303058] city--18446744073709551615---18446744073709551615---18446744073709551615
2017-03-13 23:08:28.183 自定义对象copy[4284:303058] city--18446744073709551615---18446744073709551615---18446744073709551615
结论:copy和mutableCopy均为深拷贝,拷贝后引用计数为1,retain后再+1,为2。
五、一些需要注意的点
理解深拷贝和完全拷贝
深复制,就是把原有对象内容直接克隆一份新的对象,但是这里有一个坑,就是深复制只是复制一层对象,而不是复制第二层或者更深层的对象。可能说的有点不好理解,下面看这个例子。
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects:strM1,strM2, nil];
NSMutableString *strM3 = [arrM1 objectAtIndex:0];
[strM3 appendString:@"1"];
NSMutableArray *arrM2 = [arrM1 mutableCopy];
NSLog(@"strM1--%@",arrM1);
NSLog(@"strM2--%@",arrM2);
}
看结果
2017-03-14 00:22:02.260 深复制和完全复制[5522:393850] strM1--(
11,
2
)
2017-03-14 00:22:02.261 深复制和完全复制[5522:393850] strM2--(
11,
2
)
结果:大家可能会想,为什么深拷贝已经复制了对象,那么原对象为什么也跟着变?这里就是深拷贝和完全拷贝的原因,深拷贝只是拷贝了一层数组,但是里面的字符串没有拷贝,两个数组都是用的同一个地址的字符串,所以改变一个,原对象也发生了变化。可以做下面这样的修改。
NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];
查看结果
2017-03-14 00:45:45.389 深复制和完全复制[5916:425450] arrM1--(
11,
2
)
2017-03-14 00:45:45.390 深复制和完全复制[5916:425450] arrM2--(
1,
2
)
可以利用这个方法,得到的是多一层的深复制,里面的字符串地址也进行了复制,所以改变strM3的值,不影响arrM1的值。你认为这样就解决了吗?在看下面的问题。
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects:strM1,strM2, nil];
NSMutableArray *arrM2 = [NSMutableArray arrayWithObjects:strM1,strM2,arrM1, nil];
NSMutableArray *arrM3 = [[NSMutableArray alloc] initWithArray:arrM2 copyItems:YES];
NSMutableString *strM3 = [arrM1 objectAtIndex:0];
[strM3 appendString:@"1"];
NSLog(@"arrM2--%@",arrM2);
NSLog(@"arrM3--%@",arrM3);
}
查看结果
2017-03-14 00:55:57.604 深复制和完全复制[6080:438490] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 00:55:57.606 深复制和完全复制[6080:438490] arrM3--(
1,
2,
(
11,
2
)
)
结论:看这个结果,可以发现外层的深复制了,原对象和拷贝后的对象不是同一地址,再往里看一层都变化了,就没有深复制,也就是说在增加一层,NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];这个方法不能管那么多层数了。采用归档和解档可以解决这个问题。
NSMutableArray *arrM3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arrM2]];
查看结果
2017-03-14 01:17:23.204 深复制和完全复制[6396:464958] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 01:17:23.204 深复制和完全复制[6396:464958] arrM3--(
1,
2,
(
1,
2
)
)
结论:可以看到实现了完全复制,就没有层数的限制了。
理解字符串NSString的copy和strong的不同
NSString被copy和strong修饰有什么不同,不多说废话,直接上代码了。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,copy) NSString *str;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM = [[NSMutableString alloc] initWithString:@"bestDay"];
self.str = strM;
[strM appendString:@"OfThisYear"];
NSLog(@"str----%@---%p",self.str,self.str);
NSLog(@"strM----%@---%p",strM,strM);
}
@end
直接查看结果
2017-03-14 08:53:01.874 strong修饰不同[789:18128] str----bestDay---0xa796144747365627
2017-03-14 08:53:01.875 strong修饰不同[789:18128] strM----bestDayOfThisYear---0x600000071280
结论:可以看到copy修饰的str,在赋值以后,可变字符串strM发生了变化并不会影响str的值。从打印结果来看是因为二者不是一个地址,所以不会相互影响。为什么?是因为copy修饰的属性setter方法,走的是先release旧值,copy新值再赋值给成员变量,不可变copy是深拷贝,就是内容拷贝,地址变化了。不理解的可以看我的另外一篇文章ios属性修饰符的作用。接着看strong修饰的情况。
@property (nonatomic,strong) NSString *str;
直接查看结果
2017-03-14 09:03:36.756 strong修饰不同[968:29890] str----bestDayOfThisYear---0x6000002600c0
2017-03-14 09:03:36.757 strong修饰不同[968:29890] strM----bestDayOfThisYear---0x6000002600c0
结论:被strong修饰以后只是强指针引用,并未改变地址,所以str的值会随着strM进行变化,二者的地址也是相同的。
理解copy和retain的不同
在MRC下进行测试,先看代码
//copy和retain的区别
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"111",@"222",@"333", nil];
NSMutableArray *arrMRetain = [arrM retain];
NSMutableArray *arrMCopy = [arrM copy];
[arrM removeLastObject];
NSLog(@"arrMCopy--%@--%p--%lu",arrMCopy,arrMCopy,[arrMCopy retainCount]);
NSLog(@"arrMRetain--%@--%p--%lu",arrMRetain,arrMRetain,[arrMRetain retainCount]);
}
查看结果
2017-03-14 20:16:59.895 copy和retain的区别[2816:177901] arrMCopy--(
111,
222,
333
)--0x60000005cf80--1
2017-03-14 20:16:59.895 copy和retain的区别[2816:177901] arrMRetain--(
111,
222
)--0x60000005cf50--2
结论:copy是深复制,retainCount为1,retain是浅复制,retain是使原来对象引用计数加1,所以arrM和arrMRetain是同一地址,所以remove最后一个元素,arrMRetain也跟着变化了。
六、总结
通过上面的分析,大家可以记住两点:
- 原对象和拷贝对象都是不可变对象时,为浅拷贝。
- 其他情况均为深拷贝。
具体如下表所示。
致谢
非常感谢 汉斯哈哈哈 和 西木柚子 等技术大牛分享的博客,希望我写的这个文章能帮到过大家,多多交流,有事留言,谢谢大家。我走了,还要继续去搬砖。
相关资料和博文
- iOS 浅谈:深.浅拷贝与copy.strong -- 汉斯哈哈哈
- 详解iOS的深浅拷贝 -- 西木柚子
- IOS深浅拷贝的深入分析 -- omegayy的专栏
- apple官方文档