Проблема полягає в тому, що ви даєте обіцянку, яку компілятор не може довести, що ви її дотримаєте.
Отже, ви створили цю обіцянку: дзвінок copy()поверне власний тип, повністю ініціалізований.
Але потім ви реалізували copy()такий спосіб:
func copy() -> Self {
return C()
}
Тепер я підклас, який не замінює copy(). І я повертаю a C, не повністю ініціалізований Self(що я обіцяв). Тож це нічого доброго. Як щодо:
func copy() -> Self {
return Self()
}
Ну, це не буде компілювати, але навіть якби це зробило, це не було б корисно. Підклас може не мати тривіального конструктора, тому D()може бути навіть не законним. (Хоча дивіться нижче.)
Добре, а як щодо:
func copy() -> C {
return C()
}
Так, але це не повертається Self. Воно повертається C. Ви все ще не виконуєте обіцянку.
"Але ObjC може це зробити!" Ну, начебто. Здебільшого тому, що все одно, чи дотримаєш ти обіцянку так, як Свіфт. Якщо вам не вдається реалізувати copyWithZone:в підкласі, можливо, ви не зможете повністю ініціалізувати свій об'єкт. Компілятор навіть не попередить вас, що ви це зробили.
"Але більшість всього в ObjC можна перекласти на Swift, і ObjC це робить NSCopying". Так, і ось як це визначається:
func copy() -> AnyObject!
Отже, ви можете зробити те саме (тут немає причин!):
protocol Copyable {
func copy() -> AnyObject
}
Це говорить: "Я нічого не обіцяю щодо того, що ви повернете". Ви також можете сказати:
protocol Copyable {
func copy() -> Copyable
}
Це обіцянка, яку ти можеш дати.
Але ми можемо трохи подумати про С ++ і пам’ятати, що ми можемо обіцяти . Ми можемо пообіцяти, що ми та всі наші підкласи будемо реалізовувати певні типи ініціалізаторів, і Swift буде застосовувати це (і тому може довести, що ми говоримо правду):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
}
}
І саме так слід виконувати копії.
Ми можемо зробити цей крок далі, але він використовує dynamicType, і я не перевірив його широко, щоб переконатися, що це завжди те, що ми хочемо, але це повинно бути правильно:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
}
}
Тут ми обіцяємо, що є ініціалізатор, який виконує копії для нас, і тоді ми можемо під час виконання визначити, який із них викликати, надаючи нам синтаксис методу, який ви шукали.