Проблема полягає в тому, що ви даєте обіцянку, яку компілятор не може довести, що ви її дотримаєте.
Отже, ви створили цю обіцянку: дзвінок 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) {
}
}
Тут ми обіцяємо, що є ініціалізатор, який виконує копії для нас, і тоді ми можемо під час виконання визначити, який із них викликати, надаючи нам синтаксис методу, який ви шукали.