ios中的拷貝你知道多少?

簡述深淺拷貝

我們實例化的對象存儲在堆區(qū),而指向?qū)ο蟮闹羔樢话愦鎯υ跅^(qū)。我們需要知道這個前提。
??實際上拷貝分為深拷貝(one level deep copy),淺拷貝(shallow copy)和完全拷貝(real deep copy)三種。

  1. 淺拷貝:在操作中,對于被復(fù)制對象的每一層都是指針復(fù)制。
  2. 深拷貝:在操作中,對于被復(fù)制對象,至少有一層是深復(fù)制。
  3. 完全拷貝:在操作中,對于被復(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è)置方法不需多說,基本都會了吧,我就直接上圖了。

ARC 轉(zhuǎn)為 MRC.png

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é),這里只是為了好識別而已。

自定義對象文件結(jié)構(gòu).png

我們自定義了一個類 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é)

通過上面的分析,大家可以記住兩點:

  1. 原對象和拷貝對象都是不可變對象時,為淺拷貝。
  2. 其他情況均為深拷貝。

具體如下表所示。

總結(jié)

致謝

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

相關(guān)資料和博文

  1. iOS 淺談:深.淺拷貝與copy.strong -- 漢斯哈哈哈
  2. 詳解iOS的深淺拷貝 -- 西木柚子
  3. IOS深淺拷貝的深入分析 -- omegayy的專欄
  4. apple官方文檔
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、對象拷貝有兩種方式:淺復(fù)制和深復(fù)制。顧名思義,淺復(fù)制,并不拷貝對象本身,僅僅是拷貝指向?qū)ο蟮闹羔?;深?fù)制是直接...
    滴答大閱讀 872評論 0 2
  • 簡述深淺拷貝 我們實例化的對象存儲在堆區(qū),而指向?qū)ο蟮闹羔樢话愦鎯υ跅^(qū)。我們需要知道這個前提。??實際上拷貝分為...
    朽木自雕也閱讀 647評論 1 3
  • 深拷貝和淺拷貝這個問題在面試中常常被問到,而在實際開發(fā)中,只要稍有不慎,就會在這里出現(xiàn)問題。尤其對于初學(xué)者來說,我...
    西門淋雨閱讀 1,945評論 0 1
  • 前言 不敢說覆蓋OC中所有copy的知識點,但最起碼是目前最全的最新的一篇關(guān)于 copy的技術(shù)文檔了。后續(xù)發(fā)現(xiàn)有新...
    zyydeveloper閱讀 3,744評論 4 35
  • 1.淺拷貝 所謂的淺拷貝,就是指只是將對象內(nèi)存地址多了一個引用,也就是說,拷貝結(jié)束之后,兩個對象的值不僅相同,而且...
    小瓶子Zgp閱讀 1,903評論 1 2

友情鏈接更多精彩內(nèi)容