Dado que Swift admite la sobrecarga de métodos e inicializadores, puede poner varios init
juntos y usar lo que considere conveniente:
class Person {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name = "John"
}
}
Entonces, ¿por qué existiría la convenience
palabra clave? ¿Qué hace que lo siguiente sea sustancialmente mejor?
class Person {
var name:String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "John")
}
}
swift
initialization
Desmond Hume
fuente
fuente
Respuestas:
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: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:
a
es una variable de instancia no inicializada. Esto no se compilará a menos que le demosa
un valor predeterminado:o inicializar
a
en un método de inicializador:Ahora, veamos qué sucede si subclasificamos
Foo
, ¿de acuerdo?¿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 queBar
ha heredadoFoo
's inicializador,init(a: Int)
. Pero no lo hace. ¿Y cómo podría? ¿CómoFoo
'sinit(a: Int)
saber cómo asignar un valor a lab
variable queBar
agrega? No lo hace. Por lo tanto, no podemos inicializar unaBar
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 :
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 :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.
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 laBase
clase ha designado inicializador:init(a: Int, b: Int)
. Eso significa que no estamos heredando ningún inicializador deBase
:Entonces, ¿qué pasaría si heredamos de
Base
, pero seguimos adelante e implementamos un inicializador que coincide con el inicializador designadoBase
?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 todosBase
losconvenience
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 queInheritor
tiene un solo inicializador designado. Entonces, si heredamos deInheritor
, solo tendríamos que implementar ese inicializador designado, y luego heredaríamosInheritor
el inicializador de conveniencia, lo que a su vez significa que hemos implementado todosBase
los inicializadores designados y podemos heredar susconvenience
inicializadores.fuente
init(a: Int)
dejaríab
sin inicializar.Principalmente claridad. De tu segundo ejemplo,
se requiere o designado . Tiene que inicializar todas tus constantes y variables. Los inicializadores de conveniencia son opcionales y, por lo general, se pueden usar para facilitar la inicialización. Por ejemplo, supongamos que su clase Persona tiene una variable de género opcional:
donde el género es una enumeración
podrías tener inicializadores convenientes como este
Los inicializadores de conveniencia deben llamar a los inicializadores designados o requeridos en ellos. Si su clase es una subclase, debe llamar
super.init()
dentro de su inicialización.fuente
convenience
palabra clave, pero Swift todavía estaría molesto al respecto. Ese no es el tipo de simplicidad que esperaba de Apple =)Bueno, lo primero que me viene a la mente es que se usa en herencia de clase para la organización y legibilidad del código. Continuando con tu
Person
clase, piensa en un escenario como esteCon el inicializador de conveniencia, puedo crear un
Employee()
objeto sin valor, de ahí la palabraconvenience
fuente
convenience
palabras clave eliminadas, ¿Swift no obtendría suficiente información para comportarse de la misma manera?convenience
palabra clave, no puede inicializar elEmployee
objeto sin ningún argumento.Employee()
llama alconvenience
inicializador (heredado, debido a )init()
, que llamaself.init(name: "Unknown")
.init(name: String)
, también un inicializador de conveniencia paraEmployee
, llama al inicializador designado.Aparte de los puntos que otros usuarios han explicado aquí, es mi comprensión.
Siento firmemente la conexión entre el inicializador de conveniencia y las extensiones. En cuanto a mí, los inicializadores de conveniencia son más útiles cuando quiero modificar (en la mayoría de los casos hacer que sea breve o fácil) la inicialización de una clase existente.
Por ejemplo, alguna clase de terceros que usa tiene
init
cuatro parámetros, pero en su aplicación los dos últimos tienen el mismo valor. Para evitar escribir más y limpiar su código, puede definir unconvenience init
con solo dos parámetros y dentro de él invocarself.init
con los últimos parámetros con valores predeterminados.fuente
convenience
delante de mi inicializador solo porque necesito llamarself.init
desde él? Esto parece redundante y un poco inconveniente.De acuerdo con la documentación de Swift 2.1 , los
convenience
inicializadores deben cumplir con algunas reglas específicas:Un
convenience
inicializador solo puede llamar a inicializadores en la misma clase, no en superclases (solo a través, no arriba)Un
convenience
inicializador debe llamar a un inicializador designado en algún lugar de la cadenaUn
convenience
inicializador no puede cambiar CUALQUIER propiedad antes de llamar a otro inicializador, mientras que un inicializador designado debe inicializar las propiedades que introduce la clase actual antes de llamar a otro inicializador.Al usar la
convenience
palabra clave, el compilador Swift sabe que debe verificar estas condiciones; de lo contrario, no podría.fuente
convenience
palabra clave.let
propiedades). No puede inicializar propiedades. Un inicializador designado tiene la responsabilidad de inicializar todas las propiedades introducidas antes de llamar a unsuper
inicializador designado.Una clase puede tener más de un inicializador designado. Un inicializador de conveniencia es un inicializador secundario que debe llamar a un inicializador designado de la misma clase.
fuente