Dado que Swift admite la sobrecarga de métodos e inicializadores, puede poner varios initjuntos 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 conveniencepalabra 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
conveniencehistoria. 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
requiredpalabra. 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:
aes una variable de instancia no inicializada. Esto no se compilará a menos que le demosaun valor predeterminado:o inicializar
aen 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
bque se compile. Dependiendo de qué idioma está viniendo, se podría esperar queBarha 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 labvariable queBaragrega? No lo hace. Por lo tanto, no podemos inicializar unaBarinstancia 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
conveniencepalabra 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
Baseclase de ejemplo :Tenga en cuenta que tenemos tres
convenienceinicializadores 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 laBaseclase 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
Basedesignado de la clase que coincide con el inicializador, podemos heredar todosBaselosconvenienceinicializadores de la clase :El hecho de que el inicializador con la firma correspondiente esté marcado como
convenienceno hace ninguna diferencia aquí. Solo significa queInheritortiene un solo inicializador designado. Entonces, si heredamos deInheritor, solo tendríamos que implementar ese inicializador designado, y luego heredaríamosInheritorel inicializador de conveniencia, lo que a su vez significa que hemos implementado todosBaselos inicializadores designados y podemos heredar susconvenienceinicializadores.fuente
init(a: Int)dejaríabsin 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
conveniencepalabra 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
Personclase, piensa en un escenario como esteCon el inicializador de conveniencia, puedo crear un
Employee()objeto sin valor, de ahí la palabraconveniencefuente
conveniencepalabras clave eliminadas, ¿Swift no obtendría suficiente información para comportarse de la misma manera?conveniencepalabra clave, no puede inicializar elEmployeeobjeto sin ningún argumento.Employee()llama alconvenienceinicializador (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
initcuatro 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 initcon solo dos parámetros y dentro de él invocarself.initcon los últimos parámetros con valores predeterminados.fuente
conveniencedelante de mi inicializador solo porque necesito llamarself.initdesde él? Esto parece redundante y un poco inconveniente.De acuerdo con la documentación de Swift 2.1 , los
convenienceinicializadores deben cumplir con algunas reglas específicas:Un
convenienceinicializador solo puede llamar a inicializadores en la misma clase, no en superclases (solo a través, no arriba)Un
convenienceinicializador debe llamar a un inicializador designado en algún lugar de la cadenaUn
convenienceinicializador 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
conveniencepalabra clave, el compilador Swift sabe que debe verificar estas condiciones; de lo contrario, no podría.fuente
conveniencepalabra clave.letpropiedades). No puede inicializar propiedades. Un inicializador designado tiene la responsabilidad de inicializar todas las propiedades introducidas antes de llamar a unsuperinicializador 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