One often reads, that immutable classes can implement copyWithZone very efficiently in the following way:
- (id) copyWithZone:(NSZone*)zone
{
return [self retain];
}
The idea behind that implementation is obvious: The original and the copy are both immutable instances and they will always have exactly the same content, so why not let both point to the same storage by retaining the original and avoid the overhead of copying.
However, what will happen if there is a mutable subclass? With a clean architecture, where a subclass does not have to care about implementation details of its base class, the mutable subclass should be fine to implement copyWithZone in this way:
- (id) copyWithZone:(NSZone*)zone
{
MyClass* myCopy = [super copyWithZone:zone];
myCopy->myMember = [myMember copyWithZone:zone];
return myCopy;
}
But what does this mean with the above superclass implementation of copyWithZone? The subclass is mutable, so although the copy still is immutable, the original now is mutable, but the subclass copyWithZone thanks to the superclass implementation operates on a retained instance of itself: self and myCopy both point the the same instance, so if I later change the value of mutableOriginal.myMember, then that will also change immutableCopy.myMember, which is just plain wrong.
So shouldn't immutable classes better implement copyWithZone in the following way?
- (id) copyWithZone:(NSZone*)zone
{
if([[self class] isMemberOfClass:[MyBaseClass class]])
return [self retain];
else
{
MyBaseClass* myCopy = [[self alloc] init];
myCopy->myBaseMember = [myBaseMember copyWithZone:zone];
return myCopy;
}
}