在copyWithZone中为可变子类返回[self retain]是否真的安全/好主意?

Kai*_*udi 5 objective-c nscopying

人们常常会读到,不可变类可以通过以下方式非常有效地实现copyWithZone:

- (id) copyWithZone:(NSZone*)zone
{
    return [self retain];
}
Run Code Online (Sandbox Code Playgroud)

该实现背后的想法是显而易见的:原始和副本都是不可变的实例,并且它们将始终具有完全相同的内容,因此为什么不通过保留原始文件来指向同一存储并避免复制的开销.

但是,如果存在可变子类会发生什么?使用干净的体系结构,子类不必关心其基类的实现细节,可变子类应该可以通过这种方式实现copyWithZone:

- (id) copyWithZone:(NSZone*)zone
{
    MyClass* myCopy = [super copyWithZone:zone];
    myCopy->myMember = [myMember copyWithZone:zone];
    return myCopy;
}
Run Code Online (Sandbox Code Playgroud)

但这对于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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Seb*_*ian 3

你最好的选择是initWithMyImmutableObject在你的不可变超类中有一个初始化程序。然后你的子类就可以NSCopying实现

- (id) copyWithZone:(NSZone*)zone {
    return [[[self superclass] alloc] initWithMyImmutableObject:self]
}
Run Code Online (Sandbox Code Playgroud)

这样,属性的实际复制是在超类的方法中完成的,该方法可以访问需要复制的所有私有成员。