Considere las dos clases:
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
No veo por qué esto no está permitido. En última instancia, el inicializador designado de cada clase se llama con los valores que necesiten, entonces, ¿por qué necesito repetirme en B
's init
especificando un valor predeterminado para x
nuevamente, cuando la conveniencia init
en funcionará A
bien?
class
initialization
swift
Robert
fuente
fuente
Respuestas:
Esta es la Regla 1 de las reglas de "Encadenamiento de inicializadores" como se especifica en la Guía de programación de Swift, que dice:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Énfasis mío. Los inicializadores designados no pueden llamar a los inicializadores de conveniencia.
Hay un diagrama que acompaña las reglas para demostrar qué "direcciones" de inicializador están permitidas:
fuente
SCNGeometry
: puede agregarSCNGeometryElement
s solo usando el inicializador de conveniencia, por lo tanto, no se puede heredar de él.Considerar
class A { var a: Int var b: Int init (a: Int, b: Int) { print("Entering A.init(a,b)") self.a = a; self.b = b } convenience init(a: Int) { print("Entering A.init(a)") self.init(a: a, b: 0) } convenience init() { print("Entering A.init()") self.init(a:0) } } class B : A { var c: Int override init(a: Int, b: Int) { print("Entering B.init(a,b)") self.c = 0; super.init(a: a, b: b) } } var b = B()
Debido a que todos los inicializadores designados de la clase A se anulan, la clase B heredará todos los inicializadores de conveniencia de A. Por lo tanto, ejecutar esto dará como resultado
Entering A.init() Entering A.init(a:) Entering B.init(a:,b:) Entering A.init(a:,b:)
Ahora, si al inicializador designado B.init (a: b :) se le permitiera llamar al inicializador de conveniencia de la clase base A.init (a :), esto resultaría en una llamada recursiva a B.init (a:, b: ).
fuente
Es porque puede terminar con una recursividad infinita. Considerar:
class SuperClass { init() { } convenience init(value: Int) { // calls init() of the current class // so init() for SubClass if the instance // is a SubClass self.init() } } class SubClass : SuperClass { override init() { super.init(value: 10) } }
y mira:
let a = SubClass()
cuál llamará
SubClass.init()
cuál llamaráSuperClass.init(value:)
cuál llamaráSubClass.init()
.Las reglas de inicialización designadas / convenientes están diseñadas para que la inicialización de una clase siempre sea correcta.
fuente
Encontré una solución para esto. No es muy bonito, pero resuelve el problema de no conocer los valores de una superclase o querer establecer valores predeterminados.
Todo lo que tiene que hacer es crear una instancia de la superclase, utilizando la conveniencia
init
, directamente eninit
la subclase. Luego llamas al designadoinit
del super usando la instancia que acabas de crear.class A { var x: Int init(x: Int) { self.x = x } convenience init() { self.init(x: 0) } } class B: A { init() { // calls A's convenience init, gets instance of A with default x value let intermediate = A() super.init(x: intermediate.x) } }
fuente
Considere extraer el código de inicialización de su conveniente
init()
a una nueva función auxiliarfoo()
, llamefoo(...)
para hacer la inicialización en su subclase.fuente
Mire el video de WWDC "403 Intermedio Swift" a las 18:30 para una explicación detallada de los inicializadores y su herencia. Como lo entendí, considere lo siguiente:
class Dragon { var legs: Int var isFlying: Bool init(legs: Int, isFlying: Bool) { self.legs = legs self.isFlying = isFlying } convenience initWyvern() { self.init(legs: 2, isFlying: true) } }
Pero ahora considere una subclase Wyrm: Un Wyrm es un Dragón sin piernas ni alas. ¡Así que el inicializador para un Wyvern (2 patas, 2 alas) es incorrecto! Ese error se puede evitar si simplemente no se puede llamar al inicializador Wyvern de conveniencia, sino solo al inicializador designado completo:
class Wyrm: Dragon { init() { super.init(legs: 0, isFlying: false) } }
fuente
initWyvern
tiene sentido que me llamen?Wyrm
anular el número de tramos después de llamar a un inicializador de conveniencia.¿Por qué no tiene dos inicializadores, uno con un valor predeterminado?
class A { var x: Int init(x: Int) { self.x = x } init() { self.x = 0 } } class B: A { override init() { super.init() // Do something else } } let s = B() s.x // 0
fuente