在子类的子类中实现NSCopying

23

我有一个小的类继承结构,我在实现copyWithZone:方法时遇到了困难。我已经阅读了关于NSCopying的文档,但却找不到正确的答案。

假设有两个类:ShapeSquare。Square类定义如下:

@interface Square : Shape

没有什么意外。每个类都有一个属性,Shape有一个“sides”整型变量,而Square有一个“width”整型变量。下面是copyWithZone:方法的代码:

Shape

- (id)copyWithZone:(NSZone *)zone {
    Shape *s = [[Shape alloc] init];
    s.sides = self.sides;
    return s;
}

平方

- (id)copyWithZone:(NSZone *)zone {
    Square *s = (Square *)[super copyWithZone:zone];
    s.width = self.width;
    return s;
}

从文档上看,这似乎是做事情的“正确”方式。

但事实并非如此。

如果你尝试设置/访问由 copyWithZone: 方法返回的正方形的宽度属性,它将失败,并显示以下类似错误:

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'

在 Square 方法中调用 [super copyWithZone:zone]; 实际上返回的是 Shape。你甚至可以在该方法中设置宽度属性,这真是一个奇迹。

话虽如此,怎么实现子类的 NSCopying,而又不使其负责复制其父类的变量呢?

1个回答

49

在你提问后马上意识到的一件事情是...

超类(Shape)中的 copyWithZone: 实现不应该假设它是一个 Shape。所以,不要像我上面提到的错误那样实现:

- (id)copyWithZone:(NSZone *)zone {
    Shape *s = [[Shape allocWithZone:zone] init];
    s.sides = self.sides;
    return s;
}

你应该使用:

- (id)copyWithZone:(NSZone *)zone {
    Shape *s = [[[self class] allocWithZone:zone] init]; // <-- NOTE CHANGE
    s.sides = self.sides;
    return s;
}

2
你不应该使用alloc,而应该使用allocWithZone:吗? - LandedGently
4
顺便说一句,虽然内存分区的概念很有趣,但它从未真正被证明可行。虽然allocWithZone:可能是常见的签名,但现在使用alloc也完全可以。 - Craig Otis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接