I would like to share with you guys this topic which has bothered me so many times before and we all should pay special attention to.
Let’s do an experiment first.
I would create a Class called Person, set 2 properties as “Name” and “Age”.
#import <Foundation/Foundation.h> @interface Person : NSObject @property(strong,nonatomic)NSString *name; @property(nonatomic)NSUInteger age; -(instancetype)initWithName:(NSString *)name Age:(NSUInteger)age; @end
Then in the main file, I create three reference of person.
Person *personA = [[Person alloc]initWithName:@"Cong Sun" Age:24]; Person *personB = personA; Person *personC = personA;
Then if I changed the age of the personA, guess what happened? All of the ‘age’ property for those three people has changed! Let’s look into the memory to see what exactly happened
Here, we can see that all the three person’s memory address are identical, which we say that personB and personC are the Shallow Copy of personA. They are all the pointers pointing to the same memory address. So we can treat them as personA’s alias. When you use personB = personA, you are not passing all the properties of the personA to the personB, but pass the point to it.
The Deep Copy, on the contrary, is the copy of the object’s value. After deep copy, any manipulate to the object will not affect its copy. Let’s take the NSMutableArray as a simple example.(At the beginning the reason we use the Person Class is because the Array class won’t show the actual memory address on the debug console)
NSMutableArray *array1 = [[NSMutableArray alloc]init]; NSMutableArray *shallowCopy = array1; [array1 addObject:@1]; NSMutableArray *deepCopy = [[NSMutableArray alloc]initWithArray:array1]; [array1 addObject:@2];
and let’s pull out what happened on the memory
The Objective C gives us a good tool to get a deep copy using its in-built initializer. We can figure out right here in the memory, the deep copy of the array1 will not add the @2 into the array however the shallow copy, which shares the same memory address will be modified as we manipulate the array1.
So how can we avoid the shallow copy mistake when necessary? I would say using ‘alloc’. The ‘alloc’ will create a new memory space for the variable and make it point to that address. Then, if the OC has that initializer to perform the deep copy, do it, otherwise, we need to copy every single property of the object to the new object. For example, we can do the deep copy for the Person class like this:
Person *personA = [[Person alloc]initWithName:@"Cong Sun" Age:24]; Person *personB = personA; Person *deepCopyPerson = [[Person alloc]init]; //deep copy deepCopyPerson.name = personA.name; deepCopyPerson.age = personA.age; personA.age = 26;
Here we go, we can see the difference. The deepCopyPerson is not changed!
Now, see this example, we can load the memory address by using %p
NSString *a = @"abc"; NSString *b = a; NSLog(@"memory location of a = %p",a); NSLog(@"memory location of b = %p",b); a = @"def"; NSLog(@"memory location of modified a %p",a);
The NSString is also a pointer pointing to a specific memory address. So What would happen to NSString b?
At the first step, when we assign b = a, we can see they point to the same memory address. So b is a shallow copy of a. However, when we modify the value of a, we got this result
The b stays the same value! This is tricky but when we see the console, we can get the reason:
When we do a = @”def”, we are doing the same thing as
a = [[NSString alloc]initWithString:@”def”];
Which will make a point to another memory address. However the b still hold the previous memory address of a, so even it is a shallow copy, it doesn’t matter. The trick is, only the modification on the value stored in the specific memory address the pointer points to will have the effect on all of the pointers’ shallow copies 🙂
Class like NSMutableArray has the initializer like initWithArray can make the deep copy directly! Try to dig into the documentation and be lazy when you can!
The OC’s NSCopying protocol is essentially doing a deep copy. In the situation when you want to use the NSDictionary, the key has to follow the NSCopying protocol to make a deep copy into the dictionary so that it wouldn’t be changed in the future. You can view this blog for more information http://bynomial.com/blog/?p=73