El problema es que está haciendo una promesa que el compilador no puede demostrar que cumplirá.
Entonces creaste esta promesa: la llamada copy()
devolverá su propio tipo, completamente inicializado.
Pero luego lo implementó de copy()
esta manera:
func copy() -> Self {
return C()
}
Ahora soy una subclase que no se anula copy()
. Y devuelvo a C
, no completamente inicializado Self
(lo que prometí). Entonces eso no es bueno. Qué tal si:
func copy() -> Self {
return Self()
}
Bueno, eso no se compilará, pero incluso si lo hiciera, no sería bueno. La subclase puede no tener un constructor trivial, por lo que D()
puede que ni siquiera sea legal. (Aunque vea más abajo).
OK, bueno, ¿qué tal:
func copy() -> C {
return C()
}
Sí, pero eso no regresa Self
. Vuelve C
. Aún no estás cumpliendo tu promesa.
"¡Pero ObjC puede hacerlo!" Especie de. Sobre todo porque no le importa si mantienes tu promesa como lo hace Swift. Si no logra implementar copyWithZone:
en la subclase, es posible que no pueda inicializar completamente su objeto. El compilador ni siquiera le advertirá que ha hecho eso.
"Pero casi todo en ObjC se puede traducir a Swift, y ObjC sí NSCopying
". Sí, así es como se define:
func copy() -> AnyObject!
Entonces puedes hacer lo mismo (¡no hay razón para el! Aquí):
protocol Copyable {
func copy() -> AnyObject
}
Eso dice "No prometo nada sobre lo que recibes". También podrías decir:
protocol Copyable {
func copy() -> Copyable
}
Esa es una promesa que puedes hacer.
Pero podemos pensar en C ++ por un momento y recordar que hay una promesa que podemos hacer. Podemos prometer que nosotros y todas nuestras subclases implementaremos tipos específicos de inicializadores, y Swift lo hará cumplir (y así puede demostrar que estamos diciendo la verdad):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
}
}
Y así es como debes realizar copias.
Podemos dar un paso más, pero se usa dynamicType
, y no lo he probado exhaustivamente para asegurarnos de que siempre sea lo que queremos, pero debería ser correcto:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
}
}
Aquí prometemos que hay un inicializador que realiza copias por nosotros, y luego podemos en tiempo de ejecución determinar cuál llamar, dándonos la sintaxis del método que estabas buscando.