簡述深淺拷貝
我們實例化的對象存儲在堆區(qū),而指向?qū)ο蟮闹羔樢话愦鎯υ跅^(qū)。我們需要知道這個前提。
??實際上拷貝分為深拷貝(one level deep copy),淺拷貝(shallow copy)和完全拷貝(real deep copy)三種。
- 淺拷貝:在操作中,對于被復(fù)制對象的每一層都是指針復(fù)制。
- 深拷貝:在操作中,對于被復(fù)制對象,至少有一層是深復(fù)制。
- 完全拷貝:在操作中,對于被復(fù)制對象,每一層都是對象復(fù)制。
通過下圖我們來看深淺拷貝。


通過上面兩個圖我們可以這么認(rèn)為,淺拷貝就是指針層面的賦值,指針1復(fù)制為指針2,它們指向了同一個對象;深拷貝是指針和對象的全拷貝,指針1拷貝為指針2,對象1拷貝為對象2,對象1和對象2是獨立的占用不同的地址。下面我們還可以看這張我在官方文檔上借用的圖,也可以說明我上面寫的問題。

下面我們對深拷貝、淺拷貝和完全拷貝進行分析歸納和測試。
詳述深淺拷貝
在深入研究深淺拷貝之前,我們需要載體,這里我們以非可變對象(NSArray、NSString、NSDictionary)和可變對象(NSMutableArray、NSMutableString、NSMutableDictionary)以及自定義對象進行研究測試。分別研究它們的copy和mutableCopy(這里假設(shè)閱讀者已經(jīng)知道copy和mutableCopy了,不再贅述),并研究它們的引用計數(shù)。
一、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]);
}
看打印輸出結(jié)果
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----
結(jié)論:不可變字符串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]);
}
看打印輸出結(jié)果
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----
結(jié)論:變字符串NSMutableString,它的copy和mutableCopy出來的對象地址和原對象地址都不是一樣的,是深拷貝。
● MRC下的拷貝
在開始測試之前,我們需要先將工程設(shè)置為MRC模式,設(shè)置方法不需多說,基本都會了吧,我就直接上圖了。

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]);
}
看結(jié)果
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
結(jié)論:不可變字符串NSString,它的原對象和拷貝對象地址相同copy是淺拷貝,mutableCopy是深拷貝;引用計數(shù),copy以后retainCount近似無窮大的數(shù),所以不用管理它的釋放,mutableCopy后的對象計數(shù)為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]);
}
看結(jié)果
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
結(jié)論:可變字符串NSMutableString,它的copy和mutableCopy均是深拷貝;引用計數(shù),copy和mutableCopy后引用計數(shù)為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]);
}
直接看結(jié)果
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----
結(jié)論:不可變數(shù)組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]);
}
直接查看結(jié)果
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----
結(jié)論:可變數(shù)組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]);
}
看結(jié)果
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
結(jié)論:NSArray copy后為淺拷貝,mutableCopy后為深拷貝。但是需要注意,淺拷貝對象計數(shù)在原有基礎(chǔ)上+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]);
}
看結(jié)果
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
結(jié)論:可變數(shù)組NSMutableArray的copy 和 mutableCopy均為深拷貝,copy 和 mutableCopy后對象的計數(shù)為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]);
}
直接看結(jié)果
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----
結(jié)論:不可變字典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]);
}
直接看結(jié)果
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----
結(jié)論:可變字典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]);
}
看結(jié)果
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
結(jié)論:NSDictionary的copy為淺拷貝,mutableCopy為深拷貝,淺拷貝后再retain,最后dictICopy的引用計數(shù)為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]);
}
看結(jié)果
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
結(jié)論:可變字典的copy和mutableCopy均為深拷貝,深拷貝后對象引用計數(shù)為1,retain后為2。
四、自定義對象的拷貝
● ARC下的拷貝
我們先自定義一個對象,先看這個工程框架,為了看著方便,工程名字我就用漢字了,不規(guī)范,大家不要學(xué),這里只是為了好識別而已。

我們自定義了一個類 DDCity,下面我們看一下工程里的文件我都在里面寫了什么。
DDCity.h文件
#import <Foundation/Foundation.h>
@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]);
}
運行看結(jié)果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
從提示上我們看見是沒有實現(xiàn)copyWithZone 方法,這里我們就在DDCity.m中實現(xiàn)對應(yīng)的copyWithZone方法。
在DDCity.h文件中增加協(xié)議
@interface DDCity : NSObject <NSCopying, NSMutableCopying>
在DDCity.m中實現(xiàn)兩個協(xié)議方法
- (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中的代碼不動,再次運行看結(jié)果
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---<DDCity: 0x600000037020>---0x600000037020---DDCity
2017-03-12 11:32:52.070 自定義對象copy[3039:180702] cityCopy---<DDCity: 0x600000037080>---0x600000037080---DDCity
2017-03-12 11:32:52.071 自定義對象copy[3039:180702] cityMCopy---<DDCity: 0x6000000370a0>---0x6000000370a0---DDCity
結(jié)論: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;
}
查看結(jié)果
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---<DDCity: 0x608000036d60>---0x608000036d60---DDCity
2017-03-12 11:49:20.903 自定義對象copy[3304:202108] cityCopy---<DDCity: 0x608000036da0>---0x608000036da0---DDCity
2017-03-12 11:49:20.904 自定義對象copy[3304:202108] cityMCopy---<DDCity: 0x600000037560>---0x600000037560---DDCity
結(jié)論:通過增加對屬性的賦值,新拷貝的對象就擁有了原對象的屬性值。
● 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);
}
查看結(jié)果
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---<DDCity: 0x60000002ab00>---0x60000002ab00---DDCity
2017-03-13 23:08:28.182 自定義對象copy[4284:303058] cityCopy---<DDCity: 0x60000002ab60>---0x60000002ab60---DDCity
2017-03-13 23:08:28.182 自定義對象copy[4284:303058] cityMCopy---<DDCity: 0x608000028ec0>---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
結(jié)論:copy和mutableCopy均為深拷貝,拷貝后引用計數(shù)為1,retain后再+1,為2。
五、一些需要注意的點
理解深拷貝和完全拷貝
深復(fù)制,就是把原有對象內(nèi)容直接克隆一份新的對象,但是這里有一個坑,就是深復(fù)制只是復(fù)制一層對象,而不是復(fù)制第二層或者更深層的對象??赡苷f的有點不好理解,下面看這個例子。
- (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);
}
看結(jié)果
2017-03-14 00:22:02.260 深復(fù)制和完全復(fù)制[5522:393850] strM1--(
11,
2
)
2017-03-14 00:22:02.261 深復(fù)制和完全復(fù)制[5522:393850] strM2--(
11,
2
)
結(jié)果:大家可能會想,為什么深拷貝已經(jīng)復(fù)制了對象,那么原對象為什么也跟著變?這里就是深拷貝和完全拷貝的原因,深拷貝只是拷貝了一層數(shù)組,但是里面的字符串沒有拷貝,兩個數(shù)組都是用的同一個地址的字符串,所以改變一個,原對象也發(fā)生了變化??梢宰鱿旅孢@樣的修改。
NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];
查看結(jié)果
2017-03-14 00:45:45.389 深復(fù)制和完全復(fù)制[5916:425450] arrM1--(
11,
2
)
2017-03-14 00:45:45.390 深復(fù)制和完全復(fù)制[5916:425450] arrM2--(
1,
2
)
可以利用這個方法,得到的是多一層的深復(fù)制,里面的字符串地址也進行了復(fù)制,所以改變strM3的值,不影響arrM1的值。你認(rèn)為這樣就解決了嗎?在看下面的問題。
- (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);
}
查看結(jié)果
2017-03-14 00:55:57.604 深復(fù)制和完全復(fù)制[6080:438490] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 00:55:57.606 深復(fù)制和完全復(fù)制[6080:438490] arrM3--(
1,
2,
(
11,
2
)
)
結(jié)論:看這個結(jié)果,可以發(fā)現(xiàn)外層的深復(fù)制了,原對象和拷貝后的對象不是同一地址,再往里看一層都變化了,就沒有深復(fù)制,也就是說在增加一層,NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];這個方法不能管那么多層數(shù)了。采用歸檔和解檔可以解決這個問題。
NSMutableArray *arrM3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arrM2]];
查看結(jié)果
2017-03-14 01:17:23.204 深復(fù)制和完全復(fù)制[6396:464958] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 01:17:23.204 深復(fù)制和完全復(fù)制[6396:464958] arrM3--(
1,
2,
(
1,
2
)
)
結(jié)論:可以看到實現(xiàn)了完全復(fù)制,就沒有層數(shù)的限制了。
理解字符串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
直接查看結(jié)果
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
結(jié)論:可以看到copy修飾的str,在賦值以后,可變字符串strM發(fā)生了變化并不會影響str的值。從打印結(jié)果來看是因為二者不是一個地址,所以不會相互影響。為什么?是因為copy修飾的屬性setter方法,走的是先release舊值,copy新值再賦值給成員變量,不可變copy是深拷貝,就是內(nèi)容拷貝,地址變化了。不理解的可以看我的另外一篇文章ios屬性修飾符的作用。接著看strong修飾的情況。
@property (nonatomic,strong) NSString *str;
直接查看結(jié)果
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
結(jié)論:被strong修飾以后只是強指針引用,并未改變地址,所以str的值會隨著strM進行變化,二者的地址也是相同的。
理解copy和retain的不同
在MRC下進行測試,先看代碼
//copy和retain的區(qū)別
- (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]);
}
查看結(jié)果
2017-03-14 20:16:59.895 copy和retain的區(qū)別[2816:177901] arrMCopy--(
111,
222,
333
)--0x60000005cf80--1
2017-03-14 20:16:59.895 copy和retain的區(qū)別[2816:177901] arrMRetain--(
111,
222
)--0x60000005cf50--2
結(jié)論:copy是深復(fù)制,retainCount為1,retain是淺復(fù)制,retain是使原來對象引用計數(shù)加1,所以arrM和arrMRetain是同一地址,所以remove最后一個元素,arrMRetain也跟著變化了。
六、總結(jié)
通過上面的分析,大家可以記住兩點:
- 原對象和拷貝對象都是不可變對象時,為淺拷貝。
- 其他情況均為深拷貝。
具體如下表所示。

致謝
非常感謝 漢斯哈哈哈 和 西木柚子 等技術(shù)大牛分享的博客,希望我寫的這個文章能幫到過大家,多多交流,有事留言,謝謝大家。我走了,還要繼續(xù)去搬磚。