Реализация NSCopying в подклассе подкласса

У меня есть небольшая иерархия классов, в которой мне трудно реализовать copyWithZone: для. Я прочитал документацию NSCopying, и я не могу найти правильный ответ.

Возьмите два класса: Форма и Квадрат. Квадрат определяется как:

@interface Square : Shape

Не удивительно. Каждый класс имеет свойство one, Shape имеет "стороны" 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;
}

Взглянув на документацию, это, кажется, "правильный" способ сделать что-то.

Это не так.

Если вы попытались установить/получить доступ к свойству width квадрата, возвращаемого методом 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'

Вызов [super copyWithZone:zone]; в методе Square фактически возвращает форму. Это чудо, которому вы даже позволили установить свойство width в этом методе.

Как было сказано, как реализовать NSCopying для подклассов таким образом, чтобы они не отвечали за копирование переменных своего суперкласса?

Ответ 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;
}