iOS开发|深浅拷贝梳理

copy, mutableCopy

Posted by Jack on June 3, 2014

前言

  • 浅拷贝,对内存地址的拷贝,目标对象指针和原对象指针指向同一片内存区域。当内存被销毁时,所有指向这片内存的对象指针都成为了野指针,必须重新定义才可继续使用。
  • 深拷贝,对具体内容的拷贝,目标对象指针和原对象指针指向不同的内存区域,这两块内存区域内存储的值是相同的。新的内存地址由系统自主分配,新旧对象互不影响、互不干涉。

验证

下面通过代码,简单验证下:验证工程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 Test Log

  • 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);
}

NSString Test Log

  • 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);
}

NSString Test Log

  • NSArray对象进行copy操作,新旧对象指向同一块内存区域,即拷贝的是指针,是浅拷贝。
  • NSArray对象进行mutableCopy操作,新旧对象指向两块不同的内存区域,新对象开辟了一块新的内存区域,两块内存区域内存储的值是相同的,即拷贝的是内容,是深拷贝。

⚠️然后进一步来看,当我们对数组内部元素(此处指subArr)进行变动时,对原对象及拷贝对象的影响:

操作一: [subArr addObject:@"Rose"];

该操作向subArr中再添加了一个元素Rose,log如下:

NSString Test Log

操作二: subArr = [NSMutableArray arrayWithArray:@[@"Jack and Rose"]];

该操作将subArr重新进行初始化并赋值,log如下:

NSString Test 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);
}

NSString Test Log

NSString Test Log

NSString Test Log

  • 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 深拷贝(拷贝内容)