不変クラスは、次の方法で copyWithZone を非常に効率的に実装できることがよくあります。
- (id) copyWithZone:(NSZone*)zone
{
return [self retain];
}
この実装の背後にある考え方は明らかです。オリジナルとコピーはどちらも不変のインスタンスであり、常にまったく同じコンテンツを保持します。そのため、オリジナルを保持してコピーのオーバーヘッドを回避することで、両方が同じストレージを指すようにしないでください。
しかし、変更可能なサブクラスがあるとどうなるでしょうか? サブクラスがその基本クラスの実装の詳細を気にする必要がないクリーンなアーキテクチャでは、可変サブクラスは次のように copyWithZone を実装しても問題ありません。
- (id) copyWithZone:(NSZone*)zone
{
MyClass* myCopy = [super copyWithZone:zone];
myCopy->myMember = [myMember copyWithZone:zone];
return myCopy;
}
しかし、これは copyWithZone の上記のスーパークラス実装で何を意味するのでしょうか? サブクラスは可変であるため、コピーは依然として不変ですが、オリジナルは現在可変ですが、サブクラス copyWithZone は、スーパークラスの実装のおかげで、それ自体の保持されたインスタンスで動作します: self と myCopy は両方とも同じインスタンスを指します。 mutableOriginal.myMember の値を変更すると、immutableCopy.myMember も変更されますが、これは明らかに間違っています。
不変クラスは、次の方法で copyWithZone をより適切に実装するべきではないでしょうか?
- (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;
}
}