Las respuestas existentes solo cuentan la mitad de la convenience
historia. La otra mitad de la historia, la mitad que no cubre ninguna de las respuestas existentes, responde a la pregunta que Desmond ha publicado en los comentarios:
¿Por qué Swift me obligaría a poner convenience
delante de mi inicializador solo porque necesito llamar self.init
desde allí?
Lo toqué un poco en esta respuesta , en la que cubro varias de las reglas de inicialización de Swift en detalles, pero el enfoque principal estaba en la required
palabra. Pero esa respuesta seguía abordando algo que es relevante para esta pregunta y esta respuesta. Tenemos que entender cómo funciona la herencia del inicializador Swift.
Debido a que Swift no permite variables no inicializadas, no se garantiza que herede todos (o ninguno) inicializadores de la clase de la que hereda. Si subclasificamos y agregamos cualquier variable de instancia no inicializada a nuestra subclase, habremos dejado de heredar inicializadores. Y hasta que agreguemos nuestros propios inicializadores, el compilador nos gritará.
Para ser claros, una variable de instancia no inicializada es cualquier variable de instancia a la que no se le da un valor predeterminado (teniendo en cuenta que las opciones y las opciones implícitamente desenvueltas asumen automáticamente un valor predeterminado de nil
).
Entonces en este caso:
class Foo {
var a: Int
}
a
es una variable de instancia no inicializada. Esto no se compilará a menos que le demos a
un valor predeterminado:
class Foo {
var a: Int = 0
}
o inicializar a
en un método de inicializador:
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
Ahora, veamos qué sucede si subclasificamos Foo
, ¿de acuerdo?
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
¿Correcto? Agregamos una variable y agregamos un inicializador para establecer un valor para b
que se compile. Dependiendo de qué idioma está viniendo, se podría esperar que Bar
ha heredado Foo
's inicializador, init(a: Int)
. Pero no lo hace. ¿Y cómo podría? ¿Cómo Foo
's init(a: Int)
saber cómo asignar un valor a la b
variable que Bar
agrega? No lo hace. Por lo tanto, no podemos inicializar una Bar
instancia con un inicializador que no pueda inicializar todos nuestros valores.
¿Qué tiene que ver todo esto convenience
?
Bueno, veamos las reglas sobre la herencia del inicializador :
Regla 1
Si su subclase no define ningún inicializador designado, hereda automáticamente todos sus inicializadores designados de superclase.
Regla 2
Si su subclase proporciona una implementación de todos sus inicializadores designados de superclase, ya sea al heredarlos según la regla 1 o al proporcionar una implementación personalizada como parte de su definición, entonces hereda automáticamente todos los inicializadores de conveniencia de superclase.
Observe la Regla 2, que menciona los inicializadores de conveniencia.
Entonces, ¿qué la convenience
palabra clave hace indicar a nosotros, que inicializadores pueden ser heredados por las subclases que las variables de instancia sin añadir valores por defecto.
Tomemos esta Base
clase de ejemplo :
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
Tenga en cuenta que tenemos tres convenience
inicializadores aquí. Eso significa que tenemos tres inicializadores que se pueden heredar. Y tenemos un inicializador designado (un inicializador designado es simplemente cualquier inicializador que no sea un inicializador de conveniencia).
Podemos crear instancias de la clase base de cuatro maneras diferentes:
Entonces, creemos una subclase.
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
Estamos heredando de Base
. Agregamos nuestra propia variable de instancia y no le dimos un valor predeterminado, por lo que debemos agregar nuestros propios inicializadores. Hemos añadido una, init(a: Int, b: Int, c: Int)
pero no coincide con la firma de la Base
clase ha designado inicializador: init(a: Int, b: Int)
. Eso significa que no estamos heredando ningún inicializador de Base
:
Entonces, ¿qué pasaría si heredamos de Base
, pero seguimos adelante e implementamos un inicializador que coincide con el inicializador designado Base
?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
Ahora, además de los dos inicializadores que implementamos directamente en esta clase, debido a que implementamos un inicializador Base
designado de la clase que coincide con el inicializador, podemos heredar todos Base
los convenience
inicializadores de la clase :
El hecho de que el inicializador con la firma correspondiente esté marcado como convenience
no hace ninguna diferencia aquí. Solo significa que Inheritor
tiene un solo inicializador designado. Entonces, si heredamos de Inheritor
, solo tendríamos que implementar ese inicializador designado, y luego heredaríamos Inheritor
el inicializador de conveniencia, lo que a su vez significa que hemos implementado todos Base
los inicializadores designados y podemos heredar sus convenience
inicializadores.