深拷貝、淺拷貝的理解與使用場景

什么是深拷貝、淺拷貝?

通俗解釋:深拷貝是內(nèi)容拷貝,淺拷貝是地址拷貝

區(qū)別點:

深拷貝會創(chuàng)建一個新的內(nèi)存空間,拷貝的值是一樣的,但是內(nèi)存地址不一樣。
淺拷貝只是拷貝指向原來對象的地址,使原對象的引用計數(shù)+1

容器類對象與非容器類對象在深拷貝、淺拷貝方面的異同點?

什么是容器類對象?什么是非容器類對象?

像NSString、NSNumber這些不能包含其他對象的叫做非容器類對象
像NSArray、NSDictionary這些可以包含其他對象的叫容器類對象

不可變對象NSString

NSString *string1 = @"helloworld";  
NSString *string2 = [string1 copy]; // 淺拷貝  
NSString *string3 = [string1 mutableCopy]; // 深拷貝  
NSMutableString *string4 = [string1 copy]; // 淺拷貝  此處需要注意copy返回的是不可變對象,string4不能被修改 否則會發(fā)生崩潰
NSMutableString *string5 = [string1 mutableCopy]; // 深拷貝  
  
NSLog(@"string1 = %d;string2 = %d",string1,string2);  
NSLog(@"string1 = %d;string3 = %d",string1,string3);  
NSLog(@"string1 = %d;string4 = %d",string1,string4);  
NSLog(@"string1 = %d;string5 = %d",string1,string5);  

打印結(jié)果如下:

通過對比不難發(fā)現(xiàn):

  • 如果右側(cè)是copy則是淺拷貝
  • 如果右側(cè)是mutableCopy則是深拷貝

可變對象NSMutableString

上面我們使用的是不可變的NSString,下面我們再使用可變的NSMutableString對比一下:

// 如果是一個MutableString,那么無論是copy,mutableCopy,都會創(chuàng)建一個新對象。  
   NSMutableString *string1 = [NSMutableString stringWithString:@"helloworld"];  
   NSString *string2 = [string1 copy]; // 深拷貝  
   NSString *string3 = [string1 mutableCopy]; // 深拷貝  
   NSMutableString *string4 = [string1 copy]; // 深拷貝  此處需要注意copy返回的是不可變對象,string4不能被修改 否則會發(fā)生崩潰
   NSMutableString *string5 = [string1 mutableCopy]; // 深拷貝  
  
   NSLog(@"string1 = %d;string2 = %d",string1,string2);  
   NSLog(@"string1 = %d;string3 = %d",string1,string3);  
   NSLog(@"string1 = %d;string4 = %d",string1,string4);  
   NSLog(@"string1 = %d;string5 = %d",string1,string5);  

打印結(jié)果如下:

不難發(fā)現(xiàn),對于NSMutableString, 無論是copy還是mutableCopy都會創(chuàng)建一個新對象,屬于深拷貝

不可變對象NSArray

NSArray *array01 = [NSArray arrayWithObjects:@"a",@"b",@"c", nil nil];  
NSArray *copyArray01 = [array01 copy];  
NSMutableArray *mutableCopyArray01 = [array01 mutableCopy];  
   
NSLog(@"array01 = %d,copyArray01 = %d",array01,copyArray01);  
NSLog(@"array01 = %d,mutableCopyArray01 = %d",array01,mutableCopyArray01);  
  
NSLog(@"array01[0] = %d,array01[1] = %d,array01[2] = %d",array01[0],array01[1],array01[2]);  
NSLog(@"copyArray01[0] = %d,copyArray01[1] = %d,copyArray01[2] = %d",copyArray01[0],copyArray01[1],copyArray01[2]);  
NSLog(@"mutableCopyArray01[0] = %d,mutableCopyArray01[1] = %d,mutableCopyArray01[2] = %d",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);  

打印結(jié)果如下:

不難發(fā)現(xiàn),copy是淺拷貝,mutableCopy是深拷貝,不過需要注意的是容器對象的成員元素都指向相同的地址

可變對象NSMutableArray

NSMutableArray *array01 = [NSMutableArray arrayWithObjects:@"a",@"b",@"c", nil nil];  
NSArray *copyArray01 = [array01 copy];  
NSMutableArray *mutableCopyArray01 = [array01 mutableCopy];  

NSLog(@"array01 = %d,copyArray01 = %d",array01,copyArray01);  
NSLog(@"array01 = %d,mutableCopyArray01 = %d",array01,mutableCopyArray01);  
NSLog(@"array01[0] = %d,array01[1] = %d,array01[2] = %d",array01[0],array01[1],array01[2]);  
NSLog(@"copyArray01[0] = %d,copyArray01[1] = %d,copyArray01[2] = %d",copyArray01[0],copyArray01[1],copyArray01[2]);  
NSLog(@"mutableCopyArray01[0] = %d,mutableCopyArray01[1] = %d,mutableCopyArray01[2] = %d",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);  

打印結(jié)果如下:

對比可見,容器對象與非容器對象類似,可變對象的復(fù)制都是深拷貝,不可變對象copy是淺拷貝,mutableCopy是深拷貝
需要注意的是對容器而言,元素對象始終是指針復(fù)制

如何實現(xiàn)容器對象的完全深拷貝?

正如前面所說,容器對象中的元素對象無論是copy還是mutableCopy都是指針復(fù)制,如何實現(xiàn)容器對象的完全深拷貝呢?

系統(tǒng)API

系統(tǒng)為我們實現(xiàn)容器對象的完全深拷貝提供了方法

- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag

// 使用方式如下:

- (void)fullCopy {
    NSMutableArray *marray1 = [[NSMutableArray alloc] init];
    
    NSMutableString *mstr1 = [[NSMutableString alloc]initWithString:@"value1"];
    NSMutableString *mstr2 = [[NSMutableString alloc]initWithString:@"value2"];
    
    [marry1 addObject:mstr1];
    [marry1 addObject:mstr2];
    
    NSArray *marray2 = [[NSArray alloc] initWithArray:marry1 copyItems:YES];
    
    NSLog(@"marry1:%p - %@ \r\n",marry1,marry1);
    NSLog(@"marry2:%p - %@ \r\n",marray2,marray2);
    NSLog(@"數(shù)組元素地址:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"數(shù)組元素地址:value1:%p - value2:%p \r\n",marray2[0],marray2[1]);
}

歸檔解檔方法

- (void) deplyFullCopy
{
    NSMutableArray *marry1 = [[NSMutableArray alloc] init];
    
    NSMutableString *mstr1 = [[NSMutableString alloc]initWithString:@"value1"];
    NSMutableString *mstr2 = [[NSMutableString alloc]initWithString:@"value2"];
    
    [marry1 addObject:mstr1];
    [marry1 addObject:mstr2];
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:marry1];
    NSArray *marray2 = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:data error:nil];
    
    NSLog(@"marry1:%p - %@ \r\n",marry1,marry1);
    NSLog(@"marry2:%p - %@ \r\n",marray2,marray2);
    NSLog(@"數(shù)組元素地址:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"數(shù)組元素地址:value1:%p - value2:%p \r\n",marray2[0],marray2[1]);
}

自定義類如何實現(xiàn)深、淺拷貝

  • 要想實現(xiàn)對象的自定義拷貝,必須實現(xiàn)NSCopying,NSMutableCopying協(xié)議,實現(xiàn)該協(xié)議的copyWithZone方法和mutableCopyWithZone方法
@interface Person()<NSCopying, NSMutableCopying>  
  
@end  
  
@implementation Person  
  
// 對應(yīng)copy方法  
- (id)copyWithZone:(NSZone *)zone  
{  
    Person *person = [[Person allocWithZone:zone] init];  
    person.name = self.name; // 這里的self就是被copy的對象  
    person.age = self.age;  
    return person;  
}  
  
- (id)mutableCopyWithZone:(NSZone *)zone  
{  
    Person *person = [[Person allocWithZone:zone] init];  
    person.name = self.name;  
    person.age = self.age;  
    return person;  
}  
  
  
@end  

使用場景

  • 在聲明字符串屬性時盡量使用copy,如果使用strong聲明屬性,那么當(dāng)源字符串發(fā)生改變時,對應(yīng)的屬性值也會發(fā)生改變,因為他們指向同一個地址,而使用copy就能杜絕這種情況,因為對于可變字符串進行copy是深拷貝,而strong只表示持有對象,引用計數(shù)加一。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容