Реализация NSCopying в подклассе подкласса - PullRequest
22 голосов
/ 17 декабря 2010

У меня небольшая иерархия классов, для которой у меня возникают проблемы с реализацией 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 фактически возвращает Shape.Это чудо, что вы даже можете установить свойство width в этом методе.

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

1 Ответ

48 голосов
/ 17 декабря 2010

Одна из тех вещей, которые вы понимаете сразу после запроса ...

Реализация 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;
}
...