I have a small class hierarchy that I'm having trouble implementing copyWithZone:
for. I've read the NSCopying documentation, and I can't find the correct answer.
Take two classes: Shape and Square. Square is defined as:
@interface Square : Shape
No surprise there. Each class has one property, Shape has a "sides" int, and Square has a "width" int. The copyWithZone:
methods are seen below:
Shape
- (id)copyWithZone:(NSZone *)zone {
Shape *s = [[Shape alloc] init];
s.sides = self.sides;
return s;
}
Square
- (id)copyWithZone:(NSZone *)zone {
Square *s = (Square *)[super copyWithZone:zone];
s.width = self.width;
return s;
}
Looking at the documentation, this seems to be the "right" way to do things.
It is not.
If you were to try to set/access the width property of a Square returned by the copyWithZone:
method, it would fail with an error similar to the one below:
2010-12-17 11:55:35.441 Hierarchy[22617:a0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Shape setWidth:]: unrecognized selector sent to instance 0x10010c970'
Calling [super copyWithZone:zone];
in the Square method actually returns a Shape. It's a miracle you're even allowed to set the width property in that method.
That having been said, how does one implement NSCopying for subclasses in a way that does not make them responsible for copying the variables of its superclass?
One of those things you realize right after asking...
The implementation of copyWithZone:
in the superclass (Shape) shouldn't be assuming it's a Shape. So instead of the wrong way, as I mentioned above:
- (id)copyWithZone:(NSZone *)zone {
Shape *s = [[Shape allocWithZone:zone] init];
s.sides = self.sides;
return s;
}
You should instead use:
- (id)copyWithZone:(NSZone *)zone {
Shape *s = [[[self class] allocWithZone:zone] init]; // <-- NOTE CHANGE
s.sides = self.sides;
return s;
}