iOS is full of pictures and texts to show you deep and shallow copies

Posted by jimmyp3016 on Tue, 11 Jun 2019 22:48:51 +0200

I. Concept and Summary

Shallow copy

Shallow copy is the replication of memory address, so that the target object pointer and source object point to the same memory space. When memory is destroyed, several pointers pointing to this memory need to be redefined before they can be used, otherwise they will become wild pointers.

Shallow copy is a copy of the pointer pointing to the original object, so that the reference count of the original object + 1, can be understood as creating a new pointer pointing to the original object, but not creating a new object.

2. Deep copy

Deep copy refers to the specific content of the copying object, and the memory address is self-allocated. After the end of the copy, although the value of the two objects is the same, the memory address is different, and the two objects do not affect each other and do not interfere with each other.

A deep copy is a new object that copies out just the same value as the original, but whose memory address is totally different. It has nothing to do with the original object after it is created.

   

3. Summary:

Deep copy is content copy, shallow copy is pointer copy. The essential difference lies in:

  • Whether to open a new memory address
  • Does it affect the reference count of memory addresses

 

2. Example analysis

Deep and shallow copies in iOS are more complex, involving both container and non-container, mutable and mutable copies of variable and immutable objects. The following examples are used to analyze one by one:

copy and mutableCopy of non-set objects

1.1 Immutable Object NSString

- (void) noMutableNSStringTest
{
    NSString *str1 = @"test001";
    
    NSMutableString *str2 = [str1 copy];
    //copy The returned object is an immutable object. str2 It can't be modified, so it crashes.
    //[str2 appendString:@"test"];
    
    NSMutableString *str3 = [str1 mutableCopy];
    [str3 appendString:@"modify"];
    
    NSLog(@"str1:%p - %@ \r\n",str1,str1);
    NSLog(@"str2:%p - %@ \r\n",str2,str2);
    NSLog(@"str3:%p - %@ \r\n",str3,str3);
}

Print results:

2017-07-20 18:02:10.642 beck.wang[1306:169414] str1:0x106abdbd0 - test001 
2017-07-20 18:02:10.643 beck.wang[1306:169414] str2:0x106abdbd0 - test001 
2017-07-20 18:02:10.643 beck.wang[1306:169414] str3:0x608000260940 - test001modify 

Analysis: str1, str2 address is the same and different from str3 address, NSString copy is shallow copy, and the object returned by copy is immutable object; mutableCopy is deep copy.

     

1.2 Variable Object NSMutableString

- (void) mutableNSStringTest
{
    NSMutableString *mstr1 = [NSMutableString stringWithString:@"test002"];
    
    NSMutableString *mstr2 = [mstr1 copy];
    //copy The returned object is an immutable object. mstr2 It can't be modified, so it crashes.
    //[str2 appendString:@"test"];
    
    NSMutableString *mstr3 = [mstr1 mutableCopy];
    [mstr3 appendString:@"modify"];
    
    NSLog(@"mstr1:%p - %@ \r\n",mstr1,mstr1);
    NSLog(@"mstr2:%p - %@ \r\n",mstr2,mstr2);
    NSLog(@"mstr3:%p - %@ \r\n",mstr3,mstr3);
}

Print results:

2017-07-20 18:14:35.789 beck.wang[1433:180881] mstr1:0x610000075e40 - test002 
2017-07-20 18:14:35.790 beck.wang[1433:180881] mstr2:0xa323030747365747 - test002 
2017-07-20 18:14:35.790 beck.wang[1433:180881] mstr3:0x610000074480 - test002modify 

Analysis: The addresses of mstr1, mstr2 and mstr3 are different. NSMutableString object copy and mutableCopy are deep copies, and the object returned by copy is immutable.

 

2. copy and mutableCopy of Collection Objects

2.1 Immutable Object NSArray

- (void) mutableNSArrayTest
{
    NSArray *arry1 = [[NSArray alloc] initWithObjects:@"value1", @"value2",nil];
    
    NSArray *arry2 = [arry1 copy];
    NSArray *arry3 = [arry1 mutableCopy];
    
    NSLog(@"arry1:%p - %@ \r\n",arry1,arry1);
    NSLog(@"arry2:%p - %@ \r\n",arry2,arry2);
    NSLog(@"arry3:%p - %@ \r\n",arry3,arry3);
}

Print results:

2017-07-20 18:33:53.707 beck.wang[1502:194476] arry1:0x60800003b480 - (
    value1,
    value2
) 
2017-07-20 18:33:53.708 beck.wang[1502:194476] arry2:0x60800003b480 - (
    value1,
    value2
) 
2017-07-20 18:33:53.708 beck.wang[1502:194476] arry3:0x60800004cd20 - (
    value1,
    value2
) 

Analysis: Ary1, arry2 address is the same, arr3 address is different, NSArray copy is shallow copy, and the object returned by copy is immutable object; mutableCopy is deep copy.

      

2.2 Variable object NSMutableArray

- (void) NSMutableArrayTest
{
    NSMutableArray *marry1 = [[NSMutableArray alloc] initWithObjects:@"value1", @"value2",nil];
    
    NSMutableArray *marry2 = [marry1 copy];
    
    //copy The returned object is an immutable object. marry2 It can't be modified, so it crashes.
    //[marry2 addObject:@"value3"];
    
    NSMutableArray *marry3 = [marry1 mutableCopy];
    
    NSLog(@"marry1:%p - %@ \r\n",marry1,marry1);
    NSLog(@"marry2:%p - %@ \r\n",marry2,marry2);
    NSLog(@"marry3:%p - %@ \r\n",marry3,marry3);
}

Print results:

2017-07-20 18:55:43.243 beck.wang[1577:204641] marry1:0x600000048d60 - (
    value1,
    value2
) 
2017-07-20 18:55:43.244 beck.wang[1577:204641] marry2:0x600000026000 - (
    value1,
    value2
) 
2017-07-20 18:55:43.244 beck.wang[1577:204641] marry3:0x6000000494b0 - (
    value1,
    value2
) 

Analysis: The addresses of marry1, marry2 and marr3 are different. NSMutableArray object copy and mutableCopy are deep copies, and the object returned by copy is immutable.

 

Special attention is paid to the fact that for variable objects of collection classes, deep copy is not strictly deep copy, but a single-layer deep copy, that is, although a new memory address has been opened, the values stored in memory (that is, the elements in the array are still countrymen's array element values, and there is no other copy), which is called single-layer deep copy.

Examples:

- (void)singleNSMutableArrayTest
{
    NSMutableArray *marry1 = [[NSMutableArray alloc] init];
    
    NSMutableString *mstr1 = [[NSMutableString alloc]initWithString:@"value1"];
    NSMutableString *mstr2 = [[NSMutableString alloc]initWithString:@"value2"];
    
    [marry1 addObject:mstr1];
    [marry1 addObject:mstr2];
    
    NSMutableArray *marry2 = [marry1 copy];
    NSMutableArray *marry3 = [marry1 mutableCopy];
    
    NSLog(@"marry1:%p - %@ \r\n",marry1,marry1);
    NSLog(@"marry2:%p - %@ \r\n",marry2,marry2);
    NSLog(@"marry3:%p - %@ \r\n",marry3,marry3);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry2[0],marry2[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry3[0],marry3[1]);
    
    NSLog(@"\r\n------------------After modifying the original value------------------------\r\n");
    [mstr1 appendFormat:@"aaa"];
    
    NSLog(@"marry1:%p - %@ \r\n",marry1,marry1);
    NSLog(@"marry2:%p - %@ \r\n",marry2,marry2);
    NSLog(@"marry3:%p - %@ \r\n",marry3,marry3);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry2[0],marry2[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marry3[0],marry3[1]);
}

Print results:

2017-07-20 19:48:24.539 beck.wang[1750:230132] marry1:0x60800004ae00 - (
    value1,
    value2
) 
2017-07-20 19:48:24.539 beck.wang[1750:230132] marry2:0x608000023f00 - (
    value1,
    value2
) 
2017-07-20 19:48:24.539 beck.wang[1750:230132] marry3:0x60800004abc0 - (
    value1,
    value2
) 
2017-07-20 19:48:24.540 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 
2017-07-20 19:48:24.540 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 
2017-07-20 19:48:24.540 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 
2017-07-20 19:48:24.540 beck.wang[1750:230132] 
------------------After modifying the original value------------------------
2017-07-20 19:48:24.540 beck.wang[1750:230132] marry1:0x60800004ae00 - (
    value1aaa,
    value2
) 
2017-07-20 19:48:24.540 beck.wang[1750:230132] marry2:0x608000023f00 - (
    value1aaa,
    value2
) 
2017-07-20 19:48:24.540 beck.wang[1750:230132] marry3:0x60800004abc0 - (
    value1aaa,
    value2
) 
2017-07-20 19:48:24.541 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 
2017-07-20 19:48:24.541 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 
2017-07-20 19:48:24.541 beck.wang[1750:230132] Array element address:value1:0x60800006df40 - value2:0x60800006cb40 

Before modifying the original value, the addresses of marry1, marry2 and marr3 are different. Obviously, both copy and mutableCopy are deep copies, but from the print results after modifying the original value, the deep copy here is only a single-layer deep copy: a new memory address has been opened, but the value in the array still points to the original array, so that after modifying the original value, marry2 marry3's deep copy can be obtained. The values have been changed. In addition, it can be clearly seen from the printed address of array elements that the address of array elements of marry 1, marry and Marr 3 are identical before and after modification, which is more evident.

 

2.3 Thought Extension: Complete Deep Copies of Collective Objects

2.2 mentions that for objects of collection classes, deep copy is only a single-layer deep copy. Is there any way to achieve deep copy for each layer? The answer is yes. Now we can do this:

(1) Archiving and Archiving Law

- (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(@"Array element address:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marray2[0],marray2[1]);
}

Print results:

2017-07-20 20:04:38.726 beck.wang[1833:242158] marry1:0x600000048a00 - (
    value1,
    value2
) 
2017-07-20 20:04:38.726 beck.wang[1833:242158] marry2:0x600000049780 - (
    value1,
    value2
) 
2017-07-20 20:04:38.726 beck.wang[1833:242158] Array element address:value1:0x600000066300 - value2:0x600000067000 
2017-07-20 20:04:38.726 beck.wang[1833:242158] Array element address:value1:0x600000066740 - value2:0x600000066f40 

Analysis: We can see that the pointer addresses of array elements are different while new memory addresses are created, and complete deep copies are realized.

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

- (void) deplyFullCopy2
{
    NSMutableArray *marry1 = [[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(@"Array element address:value1:%p - value2:%p \r\n",marry1[0],marry1[1]);
    NSLog(@"Array element address:value1:%p - value2:%p \r\n",marray2[0],marray2[1]);
}

Print results:

2017-07-20 20:08:04.201 beck.wang[1868:246161] marry1:0x610000050320 - (
    value1,
    value2
) 
2017-07-20 20:08:04.202 beck.wang[1868:246161] marry2:0x6100002214c0 - (
    value1,
    value2
) 
2017-07-20 20:08:04.202 beck.wang[1868:246161] Array element address:value1:0x610000265600 - value2:0x610000266400 
2017-07-20 20:08:04.202 beck.wang[1868:246161] Array element address:value1:0xa003165756c61766 - value2:0xa003265756c61766 

Analysis: Ibid.

 

III. Guidelines

No1: The copy and mutableCopy methods of mutable objects are deep copies (distinguishing full deep copies from single deep copies).

No2: The copy method of immutable objects is shallow copy, and the mutableCopy method is deep copy.

No3: The objects returned by the copy method are all immutable objects.

 

Thousands of words merge into a picture

 

 

After the deep and shallow copies are finished, I will take the time to analyze the differences between memory definition sentences, which are full of pictures and texts. In view of the limited level, if there are any inappropriate points, I hope to be criticized and corrected. Thank you.

Topics: iOS