前言
- 浅拷贝,对内存地址的拷贝,目标对象指针和原对象指针指向同一片内存区域。当内存被销毁时,所有指向这片内存的对象指针都成为了野指针,必须重新定义才可继续使用。
- 深拷贝,对具体内容的拷贝,目标对象指针和原对象指针指向不同的内存区域,这两块内存区域内存储的值是相同的。新的内存地址由系统自主分配,新旧对象互不影响、互不干涉。
验证
下面通过代码,简单验证下:验证工程GitHub链接
NSString / NSMutableString
// NSString
- (void)testString{
NSString *str = @"Jack and Rose";
NSString *cStr = [str copy];
NSMutableString *mcStr = [str mutableCopy];
NSLog(@"=== NSString Test ===");
NSLog(@"str: %p : %@",str, str);
NSLog(@"cStr: %p : %@",cStr, cStr);
NSLog(@"mcStr: %p : %@",mcStr, mcStr);
}

- 对
NSString对象进行copy操作,新旧对象指向同一块内存区域,即拷贝的是指针,是浅拷贝。 - 对
NSString对象进行mutableCopy操作,新旧对象指向两块不同的内存区域,新对象开辟了一块新的内存区域,两块内存区域内存储的值是相同的,即拷贝的是内容,是深拷贝。
// NSMutableString
- (void)testMutableString{
NSMutableString *str = [NSMutableString stringWithString:@"Jack and Rose"];
NSString *cStr = [str copy];
NSMutableString *mcStr = [str mutableCopy];
NSLog(@"=== NSMutableString Test ===");
NSLog(@"str: %p : %@",str, str);
NSLog(@"cStr: %p : %@",cStr, cStr);
NSLog(@"mcStr: %p : %@",mcStr, mcStr);
}

- 对
NSMutableString对象进行copy操作,新旧对象指向两块不同的内存区域,新对象开辟了一块新的内存区域,两块内存区域内存储的值是相同的,即拷贝的是内容,是深拷贝。 - 对
NSMutableString对象进行mutableCopy操作,同copy操作一样也是深拷贝,不同点在于copy操作返回的新对象为NSString不可变类型,mutableCopy操作返回的新对象为NSMutableString可变类型。
NSArray / NSMutableArray
// NSArray
- (void)testArray{
NSMutableArray *subArr = [NSMutableArray arrayWithArray:@[@"Jack"]];
NSArray *arr = @[subArr];
NSArray *cArr = [arr copy];
NSMutableArray *mcArr = [arr mutableCopy];
NSLog(@"=== NSArray Test ===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
subArr = [NSMutableArray arrayWithArray:@[@"Jack", @"Rose"]];
NSLog(@"=== NSArray Test - subArr reinit===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
[subArr addObject:@"Rose"];
NSLog(@"=== NSArray Test - subArr add object===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
}

- 对
NSArray对象进行copy操作,新旧对象指向同一块内存区域,即拷贝的是指针,是浅拷贝。 - 对
NSArray对象进行mutableCopy操作,新旧对象指向两块不同的内存区域,新对象开辟了一块新的内存区域,两块内存区域内存储的值是相同的,即拷贝的是内容,是深拷贝。
⚠️然后进一步来看,当我们对数组内部元素(此处指subArr)进行变动时,对原对象及拷贝对象的影响:
操作一: [subArr addObject:@"Rose"];
该操作向subArr中再添加了一个元素Rose,log如下:

操作二: subArr = [NSMutableArray arrayWithArray:@[@"Jack and Rose"]];
该操作将subArr重新进行初始化并赋值,log如下:

对比控制台log可以看出,操作一对NSArray原对象和拷贝对象都产生了影响,其指向的指针没有变,但内容发生了变化;操作二对NSArray原对象和拷贝对象都没有产生影响,其指向的指针和该指针指向内存区域内的值都没有发生变化。这是为什么呢?
原因在于,NSArray在进行copy操作时,内部子元素总是以浅拷贝的方式被复制(mutableCopy同)。操作一改变了数组内子元素所指向内存区域内的值,故对应数组内的值也发生了改变;操作二虽然也改变了subArr的值,但经过重新初始化赋值后的subArr与数组内子元素已经不再指向同一块内存区域,所以改变subArr的值对数组内子元素不造成影响。
// NSMutableArray
- (void)testMutableArray{
NSMutableArray *subArr = [NSMutableArray arrayWithArray:@[@"Jack"]];
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[subArr]];
NSArray *cArr = [arr copy];
NSMutableArray *mcArr = [arr mutableCopy];
NSLog(@"=== NSMutableArray Test ===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
[subArr addObject:@"Rose"];
NSLog(@"=== NSMutableArray Test - subArr add object===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
subArr = [NSMutableArray arrayWithArray:@[@"Jack and Rose"]];
NSLog(@"=== NSMutableArray Test - subArr reinit===");
NSLog(@"arr: %p : %@",arr, arr);
NSLog(@"cArr: %p : %@",cArr, cArr);
NSLog(@"mcArr: %p : %@",mcArr, mcArr);
}



- 对
NSMutableArray对象进行copy操作,新旧对象指向两块不同的内存区域,新对象开辟了一块新的内存区域,两块内存区域内存储的值是相同的,即拷贝的是内容,是深拷贝。 - 对
NSMutableArray对象进行mutableCopy操作,同copy操作一样也是深拷贝,不同点在于copy操作返回的新对象为NSArray不可变类型,mutableCopy操作返回的新对象为NSMutableArray可变类型。 - 对数组内部子元素进行操作,影响同
NSArray
NSDictionary / NSMutableDictionary
同NSArray / NSMutableArray。
总结
| 原对象类型 | 拷贝方法 | 副本对象类型 | 是否产生新对象 | 拷贝类型 |
|---|---|---|---|---|
| NS* | copy | NS* | NO | 浅拷贝(拷贝指针) |
| NSMutable* | copy | NS* | YES | 深拷贝(拷贝内容) |
| NS* | mutableCopy | NSMutable* | YES | 深拷贝(拷贝内容) |
| NSMutable* | mutableCopy | NSMutable* | YES | 深拷贝(拷贝内容) |