浅拷贝与深拷贝

浅拷贝和深拷贝

对象的(指针指向)内存地址

浅拷贝:
第一,拷贝了指向对象的指针,拷贝出来的对象的指针和原对象的指针指向同一块内存地址.
第二,几个对象共用一块内存,当内存被回收时候,指向这块内存的对象指针需要重新定义,不然会造成野指针错误.
深拷贝:
第一,拷贝对象的具体内容,开辟新的内存,内存地址自助分配.
第二,拷贝完成之后,内存中的值是一样的,但是内存地址不一样,2个对象之间相互不影响,也不相互干涉;

小结:用通俗话来讲,
浅拷贝就是一个人和他的影子,本体被销毁了,那么影子也不存在;

深拷贝就好比一个人和他的克隆人,本体被销毁了,克隆人依然存在;

额外知识:

野指针:当前对象指针指向的内存已经被回收,但是依然有其他对象的指针指向这块内存,那么指向这块内存的对象指针就是野指针;
内存泄漏:申请内存没有释放,造成内存越来越少;常见是block,delegate,NSTimer的使用不当造成的循环引用;

注意:要实现浅拷贝和深拷贝的类必须遵循NSCopying,NSMutableCopying这2个协议,他们分别实现了以下2个协议的方法;

1
2
3
4
5
6
7
8
9
10
11
@protocol NSCopying

- (id)copyWithZone:(nullable NSZone *)zone;

@end

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

@end

验证例子

copy,mutableCopy
第一,区分拷贝之后返回的对象是可变还是不可变;
第二,区分是深拷贝还是浅拷贝;

非容器类:NSString,NSMutableString

NSString
1
2
3
4
5
6
7
8
9
10
11
NSString *str = @"123";
NSMutableString *c01 = [str copy];
// [c01 appendFormat:@"123"];//会出现崩溃,表明不可变字符串copy之后对象是不可变的
NSMutableString *c02 = [str mutableCopy];
[c02 appendFormat:@"321"];//表明mutableCopy之后的对象是可变的
NSLog(@"\nstr=%p,\nc01=%p,\nc02=%p",str,c01,c02);

打印结果:
str=0x103799078,
c01=0x103799078,
c02=0x600001904cf0

小结1.1:不可变字符串NSString:copy是浅拷贝,返回不可变字符串;mutableCopy是深拷贝,返回类型可变字符串;

NSMutableString
1
2
3
4
5
6
7
8
9
10
11
NSMutableString *muStr = [[NSMutableString alloc]initWithString:@"123"];
NSMutableString *mu001 = [muStr copy];
// [mu001 appendFormat:@"123"];
NSMutableString *mu002 = [muStr mutableCopy];
[mu002 appendFormat:@"23"];
NSLog(@"\nmuStr=%p,\nmu001=%p,\nmu002=%p",muStr,mu001,mu002);

打印结果:
muStr=0x600001904ba0,
mu001=0x9da610ee44eb2338,
mu002=0x600001904f00

小结1.2:可变字符串NSMutableString:copy和mutabCopy都是深拷贝,copy返回对象是不可变对象,mutableCopy返回的对象是可变对象;

copy返回的对象类型都是不可变类型,mutableCopy返回的类型都是可变类型,无论被拷贝的对象是可变还是不可变;
mutbalCopy都是深拷贝;
copy当被拷贝对象是不可变时候,是浅拷贝;当被拷贝的对象是可变时,是深拷贝;

容器类:NSArray,NSMutableArray

copy返回的对象类型都是不可变,被拷贝的对象是不可变时,依然是浅拷贝;被拷贝的对象是可变时,是深拷贝;
mutableCopy返回对象都是都是可变类型的,并且都是深拷贝;

NSArray
1
2
3
4
5
6
7
8
9
10
11
NSArray *arr = @[@"321"];
NSArray *a01 = [arr copy];//返回不可变对象
NSMutableArray *a02 = [arr mutableCopy];//返回可变对象
NSLog(@"\narr=%p,\narr[0]=%p,\na01=%p,\na02=%p,\na02[0]=%p",arr,arr[0],a01,a02,a02[0]);

打印结果:
arr=0x600003c9dfb0,//原对象
arr[0]=0x10fbd9098,
a01=0x600003c9dfb0,//copy浅拷贝
a02=0x6000030d0780,//mutableCopy深拷贝
a02[0]=0x10fbd9098
NSMutableArray
1
2
3
4
5
6
7
8
9
10
11
NSMutableArray *muArr = [[NSMutableArray alloc]initWithObjects:@"231", nil];
NSArray *muAr1 = [muArr copy];//返回不可变对象
NSMutableArray *muAr2 = [muArr mutableCopy];//返回可变对象
NSLog(@"\narr=%p,\narr[0]=%p,\na01=%p,\na02=%p,\na02[0]=%p",muArr,muArr[0],muAr1,muAr2,muAr2[0]);

打印结果:
muArr=0x6000017b5740,
muArr[0]=0x108071158,//
muAr1=0x600001beca90,//深拷贝
muAr2=0x6000017b58c0,//深拷贝
muAr2[0]=0x108071158//和muArr[0]相同

小结2.1:集合类深拷贝,虽然开辟了新的内存空间,但集合里面的值的内存还是指向相同的内存(指针拷贝),这叫单层深复制;

容器类实现完全深拷贝

使用归档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSMutableArray *muA = [[NSMutableArray alloc]init];
NSMutableString *table1 = [[NSMutableString alloc]initWithFormat:@"123"];
NSMutableString *table2 = [[NSMutableString alloc]initWithFormat:@"321"];
[muA addObject:table1];
[muA addObject:table2];
NSLog(@"归档前:\nmuA :%p,\nmuA[0]:%p,\nmuA[1]:%p",muA,muA[0],muA[1]);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:muA];
NSMutableArray *archiverMuA = [NSKeyedUnarchiver unarchiveObjectWithData:data];//注意,解档后的数据类型和解档前的保持一致;
NSLog(@"归档后:\narchiverMuA :%p,\narchiverMuA[0]:%p,\narchiverMuA[1]:%p",archiverMuA,archiverMuA[0],archiverMuA[1]);

打印:
2019-06-14 11:55:32.870731+0800 Copy[15136:2127544] 归档前:
muA :0x600002a32490,
muA[0]:0x600002a32250,
muA[1]:0x600002a32550
2019-06-14 11:55:32.871096+0800 Copy[15136:2127544] 归档后:
archiverMuA :0x600002a323d0,
archiverMuA[0]:0x600002a32310,
archiverMuA[1]:0x600002a32400

小结3.1:可以看出经过归档和解档之后,容器类内存地址和里面的值的内存地址和原来的不一样;

end