Deep copy and shallow copy are very important concepts in memory management. Understanding deep copy and shallow copy helps you better understand iOS memory management.

Concepts of deep copy and shallow copy

Shallow copy is a copy of the memory address, copying the pointer to the original object, the original object reference count +1. It is understood that a new pointer to the original object is created, not a completely new object. The diagram below:

A deep copy is a copy of an object. The two objects have the same value but different memory addresses, and the two objects do not affect each other:

So how do you tell if it’s a deep copy or a shallow copy? There is a very simple method: it is to print the memory address of the object to determine whether it is the same object. If the memory address is different, it represents the newly opened space, which is a deep copy. If the memory addresses are the same, no new memory space is created. Therefore, it is a shallow copy.

The following uses an array as an example to verify the deep copy and shallow copy in different cases:

A copy of an array containing elements of the primitive type

NSArray&Copy
NSString *str2 = @"World";
NSArray *array1 = [[NSArray alloc] initWithObjects:str1, str2, nil];
id array2 = [array1 copy];
NSLog(@"array1 address: %p, array2 address: %p", array1, array2);
Copy the code

The print result is as follows

array1 address: 0x6000037ab3e0, array2 address: 0x6000037ab3e0

Array1 and array2 point to the same memory address. Array1 and array2 point to the same memory address.

NSMutableArray&Copy
NSString *str1 = @"Hello";
NSString *str2 = @"World";
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:str1, str2, nil];
id array2 = [array1 copy];
array1[0] = @"Test";
NSLog(@"array1 address: %p, array2 address: %p", array1, array2);    NSLog(@"array1: %@, array2: %@", array1, array2);
Copy the code

Print result:

array1 address: 0x600002b3daa0, array2 address: 0x600002541fe0
array1: (
Test,
World
), array2: (
Hello,
World
)
Copy the code

Array2 is of type:

Conclusion: Copying a mutable array opens up new memory and (very carefully) generates a new immutable array. The two arrays do not affect each other.

NSArray&MutableCopy

NSString *str1 = @"Hello";
NSString *str2 = @"World";
NSArray *array1 = [[NSArray alloc] initWithObjects:str1, str2, nil];
id array2 = [array1 mutableCopy];
NSLog(@"array1 address: %p, array2 address: %p", array1, array2);
Copy the code

Print result:

array1 address: 0x6000032a2fc0, array2 address: 0x600003cd2e50
Copy the code

Array2 is of type:

Conclusion: Mutablecopy opens up a new memory space for an immutable array.

NSMutableArray&mutableCopy
NSString *str1 = @"Hello";
NSString *str2 = @"World";
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:str1, str2, nil];
id array2 = [array1 mutableCopy];
NSLog(@"array1 address: %p, array2 address: %p", array1, array2);
Copy the code

Print result:

array1 address: 0x6000038cc930, array2 address: 0x6000038cc7e0

Conclusion: mutableCopy opens up a new memory space and generates a mutable array. The two arrays do not affect each other

The following summarizes the results of copy and mutableCopy

copy mutableCopy
NSString Nsstrings shallow copy NSMutableString deep copy
NSMutableString Nsstrings deep copy NSMutableString deep copy
NSArray NSArray shallow copy NSMutableArray deep copy
NSMutableArray NSArray deep copy NSMutableArray deep copy
NSDictionary NSDictionary shallow copy NSMutableDictionary deep copy
NSMutableDictionary NSDictionary shallow copy NSMutableDictionary deep copy

An array element is an array copy of a mutable string

We’ve already tried copying arrays with primitive types, but will changing strings into mutable strings follow these rules? Let’s take a look:

NSMutableString *str1 = [[NSMutableString alloc] initWithString:@"hello"];
NSMutableString *str2 = [[NSMutableString alloc] initWithString:@"world"];
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:str1, str2, **nil**];
id array2 = [array1 mutableCopy];
[str1 appendString:@"test"];
NSLog(@"array1 address: %p, array2 address: %p", array1, array2);
NSLog(@"array1 address: %@, array2 address: %@", array1, array2);
Copy the code

Print result:

array1 address: 0x600000423f90, array2 address: 0x600000423ea0

array1 address: ( hellotest, world ), array2 address: ( hellotest, world )

Array2 is a mutable array. Array1 and array2 have different memory addresses, which proves that the array is indeed deep-copied. But append str1 affects both arrays. Array1 and array2 (array1 and array2)

2021-11-30 15:20:46.047205+0800 Test124[73408:18976749] 0x6000022C16B0 2021-11-30 15:20:46.047354+0800 Test124[73408:18976749] 0x6000022c1950 2021-11-30 15:20:46.047495+0800 Test124[73408:18976749] -------- Split ----------- 2021-11-30 15:20:46.047641+0800 Test124[73408:18976749] 0x6000022C16B0 2021-11-30 15:20:46.047763+0800 Test124[73408:18976749] 0x6000022c1950Copy the code

From the data printed above, you can see that both arrays store mutable strings with the same memory address. The array itself is deeply copied, but the mutable string is only shallowly copied.

An array copy of the model elements

Person *p = [Person new];

p.age = 18;

p.name = @"mac";

p.sex = 1;

    

NSMutableArray *array1 = [NSMutableArray arrayWithObjects:p, nil];

id array2 = [array1 mutableCopy];

NSLog(@"array1 address: %p, array2 address: %p", array1, array2);

NSLog(@"array1 address: %@, array2 address: %@", array1, array2);
Copy the code

The printed result is:

2021-11-30 15:28:32.572374+0800 Test124[77676:18993723] array1 address: 0x6000034c6610, array2 address: 0x6000034c65B0 2021-11-30 15:28:32.572647+0800 Test124[77676:18993723] array1 address: ("<Person: 0x600003ae8240>" ), array2 address: ( "<Person: 0x600003ae8240>" )Copy the code

It turns out to be the same as the mutable string above: the array itself is deeply copied, but the internal objects are only shallowly copied

Single copy

In both cases, a deep copy of an array is not exactly a deep copy, but a single-layer deep copy. Although the array creates new memory, the elements in the array are still the values of the previous array elements, rather than being copied.

So how do you really implement deep copy?

Method 1: Go through the number group and make a deep copy of each element

Person *p1 = [Person new];

p1.age = 18;

p1.name = @"mac";

p1.sex = 1;



Person *p2 = [Person new];

p2.age = 20;

p2.name = @"jack";

p2.sex = 2;

    

NSMutableArray *array1 = [NSMutableArray arrayWithObjects:p1, p2, nil];

NSMutableArray *array2 = [NSMutableArray arrayWithCapacity:0];

for (Person *p **in** array1) {

    [array2 addObject:p.mutableCopy];

}



NSLog(@"array1 address: %p, array2 address: %p", array1, array2);

NSLog(@"array1 address: %@, array2 address: %@", array1, array2);
Copy the code

Print result:

2021-11-30 15:39:02.364721+0800 Test124[83529:19016781] array1 address: 0x6000024a5740, array2 address: 0x6000024a5860

2021-11-30 15:39:02.364979+0800 Test124[83529:19016781] array1 address: (

“<Person: 0x600002ad1b60>”,

“<Person: 0x600002ad1b40>”

), array2 address: (

“<Person: 0x600002ad1b80>”,

“<Person: 0x600002ad1ba0>”

)

As you can see from the printed result, array2 is deeply copied, as are the elements inside Array2. Also note that custom model classes need to implement the NSCopying protocol or they will crash.

Method 2: initWithArray:copyItems

Method one, which requires traversal of elements, is not very friendly. InitWithArray :copyItems is an alternative that requires the model class to implement the NSCopying protocol. InitWithArray: Deep copies are made of the properties of the model itself, but this method does not work if the model properties also contain arrays.

Method three: file solution

The principle of archiving unfile is to write data from memory to a local file. When reading data from a local file, it must create new memory space. If the model is deeply nested, archive unfiles can be used for deep copy. The following methods should be rewritten in compliance with the NSSecureCoding protocol when using archiving files