23

実装copyWithZone:に問題がある小さなクラス階層があります。NSCopying のドキュメントを読みましたが、正しい答えが見つかりません。

ShapeSquareの 2 つのクラスを使用します。正方形は次のように定義されます。

@interface Square : Shape

そこに驚きはありません。各クラスには1 つのプロパティがあり、Shape には "sides" int があり、Square には "width" int があります。メソッドは以下のcopyWithZone:とおりです。

- (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:メソッドによって返される Square の width プロパティを設定またはアクセスしようとすると、次のようなエラーで失敗します。

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'

[super copyWithZone:zone];Square メソッドを呼び出すと、実際には Shape が返されます。そのメソッドで width プロパティを設定することさえ許可されているのは奇跡です。

そうは言っても、スーパークラスの変数をコピーする責任を負わせないように、サブクラスにNSCopying を実装するにはどうすればよいでしょうか?

4

1 に答える 1

49

質問した直後に気付くことの1つ...

copyWithZone:スーパークラス ( Shape ) での の実装は、それが 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;
}
于 2010-12-17T17:07:25.000 に答える