iOS - 深拷贝浅拷贝
一、基本概念
浅拷贝
浅拷贝只是对 object
对象指针进行拷贝,不会开辟新的内存。与数据源指向的是同一内存。例如 copyA = [A copy]
,capyA
和 A
指向的是同一内存,A
的值变化,copyA
的值也会发生变化。
copy
方法做的是浅拷贝。
深拷贝
深拷贝会开辟新的内容,然后对原数据进行复制,新 object
对象与数据源指向的是不同的内存,对数据源操作不会影响新object
对象。例如 mutableCopyA = [A mutableCopy]
,mutableCopyA
和 A
指向的是两块不同的内存,A
的值发生变化不会影响到 mutableCopyA
的值。
mutableCopy
方法做的是深拷贝。
�####二、不同对象类型的深拷贝和浅拷贝
1.NSString
和 NSMutableString
NSString
// 不可变字符串
NSString *str = [NSString stringWithFormat:@"哈哈哈哈哈"];
NSLog(@"str = %@ :对象地址==%p ,对象指针地址==%p",str ,str, &str);
NSString *strCopy = [str copy];
NSString *strMutableCopy = [str mutableCopy];
str = [NSString stringWithFormat:@"测试"];
NSLog(@"str = %@ :对象地址==%p ,对象指针地址==%p",str ,str, &str);
NSLog(@"strCopy = %@ :对象地址==%p , 对象指针地址==%p", strCopy,strCopy, &strCopy);
NSLog(@"mutalbeCopy = %@ :对象地址==%p , 对象指针地址==%p",strMutableCopy, strMutableCopy, &strMutableCopy);
输出结果
2022-07-18 15:38:47.952442+0800 DJTestDemo[28524:358667] str = 哈哈哈哈哈 :对象地址==0x60000394f540 ,对象指针地址==0x7ff7bdb61b38
2022-07-18 15:38:47.952607+0800 DJTestDemo[28524:358667] str = 测试 :对象地址==0x600003715ae0 ,对象指针地址==0x7ff7bdb61b38
2022-07-18 15:38:51.508690+0800 DJTestDemo[28524:358667] strCopy = 哈哈哈哈哈 :对象地址==0x60000394f540 , 对象指针地址==0x7ff7bdb61b30
2022-07-18 15:38:51.508918+0800 DJTestDemo[28524:358667] mutalbeCopy = 哈哈哈哈哈 :对象地址==0x60000394f300 , 对象指针地址==0x7ff7bdb61b28
针对 NSString
类型来看,copy
后对象地址没有变化,说明copy
只是创建了新的指针,指针指向的还是原来的内存地址。mutalbeCopy
之后对象地址也发生了变化,说明 mutalbeCopy
创建了新的指针,同时开辟了新的内存并将 string
的内容复制了过去。
NSMutableString
// 可变字符串
NSMutableString *mutableString = [NSMutableString stringWithString:@"hello world"];
NSString *mutableStringCopy = [mutableString copy];
NSMutableString *mutableStringMutableCopy = [mutableString mutableCopy];
NSLog(@"mutableString: 对象地址==%p , 对象指针地址==%p", mutableString, &mutableString);
NSLog(@"copy: 对象地址==%p , 对象指针地址==%p", mutableStringCopy, &mutableStringCopy);
NSLog(@"mutalbeCopy: 对象地址==%p , 对象指针地址==%p", mutableStringMutableCopy, &mutableStringMutableCopy);
输出结果
2022-07-18 16:00:45.717067+0800 DJTestDemo[30814:386885] mutableString: 对象地址==0x60000233a6d0 , 对象指针地址==0x7ff7be10cca8
2022-07-18 16:00:45.717308+0800 DJTestDemo[30814:386885] copy: 对象地址==0x600002d10380 , 对象指针地址==0x7ff7be10cca0
2022-07-18 16:00:45.717418+0800 DJTestDemo[30814:386885] mutalbeCopy: 对象地址==0x60000233aa30 , 对象指针地址==0x7ff7be10cc98
针对 NSMutableString
类型来看,不论是 copy
还是 mutalbeCopy
,对象的指针地址和对象地址都发生了变化,因此两种形式都开辟了新内存并将内容复制到的新内存中。
2. NSArray
和 NSMutableArray
按照上面 NSString
和 NSMutableString
系统非集合类对象的思路,对 NSArray
和 NSMutableArray
系统集合类对象做同样的示例分析。
因为集合类对象里面的元素还可以是对象,因此需要打印内部元素的地址信息,查看是否进行了深拷贝。
NSArray
NSArray *cellArray1 = @[@"1", @"2", @"3"];
NSArray *cellArray2 = @[@"4", @"5", @"6"];
NSArray *array = @[cellArray1, cellArray2];
NSArray *arrayCopy = [array copy];
NSArray *arrayMutableCopy = [array mutableCopy];
NSArray *tempArray = array.firstObject;
NSArray *tempArrayCopy = arrayCopy.firstObject;
NSArray *tempArrayMutableCopy = arrayMutableCopy.firstObject;
NSLog(@" array:对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", array, &array, tempArray, &tempArray);
NSLog(@" copy: 对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", arrayCopy, &arrayCopy, tempArrayCopy, &tempArrayCopy);
NSLog(@"mutalbeCopy: 对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", arrayMutableCopy, &arrayMutableCopy, tempArrayMutableCopy, &tempArrayMutableCopy);
输出结果
2022-07-18 16:13:17.254829+0800 DJTestDemo[32009:406468] array:对象地址 == 0x600002343180 ,对象指针地址 == 0x7ff7b3413b10 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413af8
2022-07-18 16:13:17.254992+0800 DJTestDemo[32009:406468] copy: 对象地址 == 0x600002343180 ,对象指针地址 == 0x7ff7b3413b08 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413af0
2022-07-18 16:13:17.255115+0800 DJTestDemo[32009:406468] mutalbeCopy: 对象地址 == 0x600002d1d470 ,对象指针地址 == 0x7ff7b3413b00 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413ae8
NSMutableString
NSArray *cellArray1 = @[@"1", @"2", @"3"];
NSArray *cellArray2 = @[@"4", @"5", @"6"];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[cellArray1, cellArray2]];
NSArray *mutableArrayCopy = [mutableArray copy];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
NSMutableArray *tempMutableArray = mutableArray.firstObject;
NSMutableArray *tempMutableArrayCopy = mutableArrayCopy.firstObject;
NSMutableArray *tempMutableArrayMutableCopy = mutableArrayMutableCopy.firstObject;
NSLog(@"mutableArray: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArray, &mutableArray, tempMutableArray, &tempMutableArray);
NSLog(@" copy: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArrayCopy, &mutableArrayCopy, tempMutableArrayCopy, &tempMutableArrayCopy);
NSLog(@" mutalbeCopy: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArrayMutableCopy, &mutableArrayMutableCopy, tempMutableArrayMutableCopy, &tempMutableArrayMutableCopy);
输出结果
2022-07-18 16:18:30.292810+0800 DJTestDemo[32109:412361] mutableArray: 对象地址 == 0x6000022bcd80 , 对象指针地址 == 0x7ff7b9f8db10 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8daf8
2022-07-18 16:18:30.292948+0800 DJTestDemo[32109:412361] copy: 对象地址 == 0x600002cf3520 , 对象指针地址 == 0x7ff7b9f8db08 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8daf0
2022-07-18 16:18:30.293075+0800 DJTestDemo[32109:412361] mutalbeCopy: 对象地址 == 0x6000022bcdb0 , 对象指针地址 == 0x7ff7b9f8db00 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8dae8
结论
copy
和 mutableCopy
之后对象地址的变化与非集合对象变化相同。
无论是 copy
还是 mutableCopy
,内部元素的地址都没有变化,只是指针地址发生了变化。
mutableCopy
做的也只是浅层深拷贝。
3.自定义对象的深拷贝和浅拷贝
首先自定义对象需要实现 NSCopying
和 NSMutableCopying
协议。
#import <Foundation/Foundation.h>
@interface DJExample : NSObject
@property (nonatomic, strong) NSString *exampleTitle;
@end
#import "DJExample.h"
@interface DJExample()<NSCopying,NSMutableCopying>
@end
@implementation DJExample
// 实现copyWithZone方法
- (id)copyWithZone:(NSZone *)zone {
DJExample *example = [[self class] allocWithZone:zone];
example.exampleTitle = self.exampleTitle;
return example;
}
// 实现mutableCopyWithZone方法
- (id)mutableCopyWithZone:(NSZone *)zone {
DJExample *example = [[self class] allocWithZone:zone];
example.exampleTitle = self.exampleTitle;
return example;
}
@end
DJExample *example1 = [[DJExample alloc] init];
example1.exampleTitle = @"test1";
DJExample *exampleCopy = [example1 copy];
DJExample *exampleMutableCopy = [example1 mutableCopy];
NSString *title = example1.exampleTitle;
NSString *titleCopy = exampleCopy.exampleTitle;
NSString *titleMutableCopy = exampleMutableCopy.exampleTitle;
NSLog(@" Example: 对象地址 == %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", example1, &example1, title, &title);
NSLog(@" copy: 对象地址 == %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", exampleCopy, &exampleCopy, titleCopy, &titleCopy);
NSLog(@"mutalbeCopy: 对象地址== %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", exampleMutableCopy, &exampleMutableCopy, titleMutableCopy, &titleMutableCopy);
输出结果
2022-07-18 16:42:58.667753+0800 DJTestDemo[32713:434463] Example: 对象地址 == 0x6000034883a0 ,对象指针地址 == 0x7ff7b90b4b38 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b20
2022-07-18 16:42:58.667900+0800 DJTestDemo[32713:434463] copy: 对象地址 == 0x6000034883b0 ,对象指针地址 == 0x7ff7b90b4b30 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b18
2022-07-18 16:42:58.668013+0800 DJTestDemo[32713:434463] mutalbeCopy: 对象地址== 0x6000034883c0 ,对象指针地址 == 0x7ff7b90b4b28 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b10
自定义对象使用 copy
或 mutableCopy
之后,对象的地址和指针地址都发生了变化,但是属性地址还是指向原来的地址,也就是说实际也是浅层的深拷贝。
因为 copyWithZone
方法和 mutableCopyWithZone
都是自己重写的,所以其实自定义对象使用 copy
还是 mutableCopy
并没有什么区别。
4.数组中的元素是自定义对象时的深拷贝和浅拷贝
DJExample *example2 = [[DJExample alloc] init];
example2.exampleTitle = @"test2";
NSArray *exampleArray = [[NSArray alloc] initWithObjects:example2, nil];
NSArray *shallowCopyArray = [[NSArray alloc] initWithArray:exampleArray];
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:exampleArray copyItems:YES];
DJExample *shallowCopyExample = shallowCopyArray.firstObject;
DJExample *deepCopyExample = deepCopyArray.firstObject;
example2.exampleTitle = @"where is test2";
NSLog(@" exampleArray:对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", exampleArray, &exampleArray, example2, &example2, example2.exampleTitle);
NSLog(@"shallowCopyArray: 对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", shallowCopyArray, &shallowCopyArray, shallowCopyExample, &shallowCopyExample, shallowCopyExample.exampleTitle);
NSLog(@" deepCopyArray: 对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", deepCopyArray, &deepCopyArray, deepCopyExample, &deepCopyExample, deepCopyExample.exampleTitle);
输出结果
2022-07-18 16:51:22.639103+0800 DJTestDemo[32916:442954] exampleArray:对象地址 == 0x600003298d20 , 对象指针地址 == 0x7ff7b9279b30 , 属性地址 == 0x6000030b0190 , 属性指针地址 == 0x7ff7b9279b38 , 属性值 == where is test2
2022-07-18 16:51:22.639313+0800 DJTestDemo[32916:442954] shallowCopyArray: 对象地址 == 0x600003298d20 , 对象指针地址 == 0x7ff7b9279b28 , 属性地址 == 0x6000030b0190 , 属性指针地址 == 0x7ff7b9279b18 , 属性值 == where is test2
2022-07-18 16:51:22.639472+0800 DJTestDemo[32916:442954] deepCopyArray: 对象地址 == 0x6000030b01d0 , 对象指针地址 == 0x7ff7b9279b20 , 属性地址 == 0x6000030b0160 , 属性指针地址 == 0x7ff7b9279b10 , 属性值 == test2
从运行结果中可以看出,使用 initWithArray:
方法实现的是浅拷贝,原对象的值发生改变新数组中也会同步改变。
使用 initWithArray: copyItems:
方法实现的是深拷贝,数组和数组中的对象都会开辟新的内存,因此原数组中的数据发生变化,新数组不会变化。数组中的自定义对象需要实现NSCopying 和 NSMutableCopying 协议。