ajrlewis
ajrlewis

Reputation: 3058

once and for all: deep copying objects

i have seen many attempts to answer this on Stackoverflooooooow and none have satisfied me.

i have a Person class with only a public name attribute that subclasses NSObject. in the main i run:

Person *alex = [[Person alloc] init];
alex.name = @"alex"
NSLog(@"name: %@ %p", alex.name, alex.name");

which will print to the console

2015-08-21 21:53:34.047 Test[29456:5960428] name: alex 0x108dfd640

the two methods i've found for copying both implement the copyWithZone: selector from the <NSCopying> protocol, which i place this in the Person class.

1) the first version of this selector is:

- (id) copyWithZone:(NSZone *) zone
{
    Person *personCopy = [[[self class] allocWithZone:zone] init];
    personCopy.name = [self.name copyWithZone:zone];
    // i've also tried: personCopy.name = [self.name copy];
    return personCopy;
}

in the main i run

Person *aCopyOfAlex = [alex copy]
NSLog(@"name: %@ %p", aCopyOfAlex.name, aCopyOfAlex.name");

which logs

2015-08-21 21:53:34.047 Test[29456:5960428] name: alex 0x108dfd640

the Person copy instance is different but the name attributes for the copy and original point to the same place.

2) the second version of this selector uses an archiver (which i guess takes some time - especially if i have a lot of attributes to deep copy across?) and then an unarchiver:

- (id) copyWithZone:(NSZone *) zone
{
    Person *personCopy = [[[self class] allocWithZone:zone] init];
    NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject:self.name];
    personCopy.name = [NSKeyedUnarchiver unarchiveObjectWithData:buffer];
    return personCopy;
}

in the main i run again

Person *aCopyOfAlex = [alex copy]
NSLog(@"name: %@ %p", aCopyOfAlex.name, aCopyOfAlex.name");

which logs

2015-08-21 21:53:34.047 Test[29456:5960428] name: alex 0x7fa80969e4a0

this time both the Person copy instance and the name attribute point to different places.

finally to a question! can someone say which method is correct (if either), please? it seems the second does what i want and would be an actual deep copy..

thanks!

Upvotes: 0

Views: 45

Answers (2)

pevasquez
pevasquez

Reputation: 862

Although I have to agree with @rmaddy about the second one being overkill for this exact case, it is the only one (from the two you are posting) that will perform a deep copy.

Please see What is the difference between a deep copy and a shallow copy? for a nice explanation of what deep copying is.

If you had a mutable property say NSMutableArray *addresses, the first method would leave you with two Person instances sharing the same NSMutableArray instances, which means that if you mutate addresses you would see changes in both Person instances, as they are indeed sharing the same array.

Please note that the second one will depend on whether the properties in Person are indeed archivable.

Upvotes: 0

rmaddy
rmaddy

Reputation: 318804

There's nothing wrong with your first bit of code. What you are seeing is an optimization done with strings. Since you started with an immutable string literal, copying it simply gives you back the same object. This isn't a problem because it is immutable.

Your second implementation of Person copyWithZone: is overkill. Don't bother with the archiving.

Upvotes: 2

Related Questions