iOS深浅拷贝

作者: 朽木自雕也 | 来源:发表于2018-09-12 00:47 被阅读52次

    简述深浅拷贝

    我们实例化的对象存储在堆区,而指向对象的指针一般存储在栈区。我们需要知道这个前提。
      实际上拷贝分为深拷贝(one level deep copy),浅拷贝(shallow copy)和完全拷贝(real deep copy)三种。

    1. 浅拷贝:在操作中,对于被复制对象的每一层都是指针复制。
    2. 深拷贝:在操作中,对于被复制对象,至少有一层是深复制。
    3. 完全拷贝:在操作中,对于被复制对象,每一层都是对象复制。

    通过下图我们来看深浅拷贝。

    image image

    通过上面两个图我们可以这么认为,浅拷贝就是指针层面的赋值,指针1复制为指针2,它们指向了同一个对象;深拷贝是指针和对象的全拷贝,指针1拷贝为指针2,对象1拷贝为对象2,对象1和对象2是独立的占用不同的地址。下面我们还可以看这张我在官方文档上借用的图,也可以说明我上面写的问题。

    image

    下面我们对深拷贝、浅拷贝和完全拷贝进行分析归纳和测试。

    详述深浅拷贝

    在深入研究深浅拷贝之前,我们需要载体,这里我们以非可变对象(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模式,设置方法不需多说,基本都会了吧,我就直接上图了。

    image

    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下的拷贝

    我们先自定义一个对象,先看这个工程框架,为了看着方便,工程名字我就用汉字了,不规范,大家不要学,这里只是为了好识别而已。

    [图片上传失败...(image-620402-1536682850056)]

    我们自定义了一个类 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]);
    }
    
    

    运行看结果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 <NSCopying, NSMutableCopying>

    在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---<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
    
    

    结论: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---<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
    
    

    结论:通过增加对属性的赋值,新拷贝的对象就拥有了原对象的属性值。

    ● 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---<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
    
    

    结论: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也跟着变化了。

    六、总结

    通过上面的分析,大家可以记住两点:

    1. 原对象和拷贝对象都是不可变对象时,为浅拷贝。
    2. 其他情况均为深拷贝。

    具体如下表所示。

    [图片上传失败...(image-d97dd6-1536682850056)]

    致谢

    非常感谢 汉斯哈哈哈西木柚子 等技术大牛分享的博客,希望我写的这个文章能帮到过大家,多多交流,有事留言,谢谢大家。我走了,还要继续去搬砖。

    相关资料和博文

    1. iOS 浅谈:深.浅拷贝与copy.strong -- 汉斯哈哈哈
    2. 详解iOS的深浅拷贝 -- 西木柚子
    3. IOS深浅拷贝的深入分析 -- omegayy的专栏
    4. apple官方文档

    刀客传奇

    相关文章

      网友评论

      本文标题:iOS深浅拷贝

      本文链接:https://www.haomeiwen.com/subject/zhapgftx.html