Objective C study note

Deep Copy and Shallow Copy in Objective C

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

Screen Shot 2015-06-07 at 6.05.52 PM

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

Screen Shot 2015-06-07 at 6.21.10 PM

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;

Screen Shot 2015-06-07 at 6.33.24 PM

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?

Screen Shot 2015-06-09 at 1.11.47 PM

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

Screen Shot 2015-06-09 at 1.17.44 PM

The b stays the same value! This is tricky but when we see the console, we can get the reason:

Screen Shot 2015-06-09 at 1.16.27 PM

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 🙂

Tips:

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

Advertisements

6 thoughts on “Deep Copy and Shallow Copy in Objective C

  1. I really like this post, it was very clear, and the screenshots help a lot. I was wondering about the mutability of the copies. I understand that they are immutable (after the first level – that one will retain the mutability of the original). I wonder if you have thoughts about making a recursive (or not) Deep Mutable Copy in Obj-C…?

    Liked by 1 person

    1. The NSString is not a constant,the difference with the NSString and a NSMutableString is whether we are allowed to change the content in the current memory address. For NSString, you can still change the value however it will not overwrite the current memory address, instead, it will allocate a new piece of memory and point to that.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s