Best practice when implementing copyWithZone:

fuzzygoat picture fuzzygoat · Mar 28, 2012 · Viewed 48.4k times · Source

I am trying to clear up a few things in my head about implementing copyWithZone:, can anyone comment on the following ...

// 001: Crime is a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [[[self class] allocWithZone:zone] init];
    if(newCrime) {
        [newCrime setMonth:[self month]];
        [newCrime setCategory:[self category]];
        [newCrime setCoordinate:[self coordinate]];
        [newCrime setLocationName:[self locationName]];
        [newCrime setTitle:[self title]];
        [newCrime setSubtitle:[self subtitle]];
    }
    return newCrime;
}

// 002: Crime is not a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    [newCrime setMonth:[self month]];
    [newCrime setCategory:[self category]];
    [newCrime setCoordinate:[self coordinate]];
    [newCrime setLocationName:[self locationName]];
    [newCrime setTitle:[self title]];
    [newCrime setSubtitle:[self subtitle]];
    return newCrime;
}

In 001:

  1. Is it best to write the class name directly [[Crime allocWithZone:zone] init] or should I use [[[self Class] allocWithZone:zone] init]?

  2. Is it ok to use [self month] for copying the iVars or should I be accessing the iVars directly i.e. _month?

Answer

Tony picture Tony · Mar 28, 2012
  1. You should always use [[self class] allocWithZone:zone] to make sure you are creating a copy using the appropriate class. The example you give for 002 shows exactly why: Subclasses will call [super copyWithZone:zone] and expect to get back an instance of the appropriate class, not an instance of the super class.

  2. I access the ivars directly, so I don't need to worry about any side effects I might add to the property setter (e.g., generating notifications) later on. Keep in mind, subclasses are free to override any method. In your example, you are sending two extra messages per ivar. I would implement it as follows:

Code:

- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    newCrime->_month = [_month copyWithZone:zone];
    newCrime->_category = [_category copyWithZone:zone];
    // etc...
    return newCrime;
}

Of course, whether you copy the ivars, retain them, or just assign them should mirror what the setters do.