Inicializadores en swift parte-1: (introducción, conveniencia e inicializadores designados)

La inicialización es un gran tema para cubrir rápidamente. Intentaré simplificarlo tanto como pueda en este artículo.

https://stocksnap.io/
Documentos de Apple: la inicialización es el proceso de preparación de una instancia de una clase, estructura o enumeración para su uso. Este proceso implica establecer un valor inicial para cada propiedad almacenada en esa instancia y realizar cualquier otra configuración o inicialización que se requiera antes de que la nueva instancia esté lista para su uso.

Las clases y estructuras deben establecer todas sus propiedades almacenadas en un valor inicial apropiado para el momento en que se crea una instancia de esa clase o estructura. Las propiedades almacenadas no se pueden dejar en un estado indeterminado.

Inicializadores

Los inicializadores son como métodos especiales a los que se puede llamar para crear una nueva instancia de un tipo particular.

En su forma más simple, un inicializador es como un método de instancia sin parámetros, escrito usando la palabra clave init:

en eso() {
// realiza alguna inicialización aquí
}

Como he mencionado antes, las propiedades almacenadas no se pueden dejar en un estado indeterminado. Se les deben asignar algunos valores al momento de la inicialización. Considere la siguiente clase:

enum Género {
caso masculino
caso femenino
caso desconocido
}
struct Human {
var gender: Gender // esta es una propiedad almacenada.
init (género: género) {
self.gender = género
}
}
let human = Human (género: .male)

Aquí estoy asignando algún valor a la variable género en el momento de la inicialización dentro del inicializador. Si no está utilizando el método init para asignar un valor a la variable género, debe hacer lo siguiente:

  • debe establecer la variable como valor predeterminado en el momento de la declaración.
var gender: Gender = .male
  • O bien, debe declarar la variable como opcional.
var gender: ¿género?

El objetivo es que, en el momento de la inicialización de una instancia, todas las propiedades deben inicializarse.

Personalización de inicialización

Podemos agregar inicializadores personalizados a una clase con parámetros de entrada personalizados y tipos de propiedades opcionales.

Podemos tener más de un inicializador basado en nuestro requisito, con diferentes nombres de parámetros, tipos, etc.
struct Human {
var gender: género
var age: Int = 10
init (género: género) {// inicializador 1
self.gender = género
}
init (edad: Int) {// inicializador 2
self.age = age
self.gender = .unknown
}
init (age: Int, gender: Gender) {// initializer 3
self.age = age
self.gender = género
}
}
// ------------------------------
let human = Human (género: .male)
let human2 = Human (edad: 20)
let human3 = Human (edad: 40, género: .male)
El compilador rápido decidirá a qué método de inicio llamar en función de la etiqueta del argumento.

Tenga en cuenta que no es posible llamar a estos inicializadores sin usar etiquetas de argumento. Las etiquetas de argumento siempre deben usarse en un inicializador si están definidas, y omitirlas es un error en tiempo de compilación:

let human4 = Human () // error: no se puede invocar el inicializador para el tipo "Human" sin argumentos
let human5 = Human (40, .male) // error: error: faltan etiquetas de argumento 'age: gender:' en la llamada

Parámetros de inicializador sin etiquetas de argumento

Si no desea utilizar una etiqueta de argumento para un parámetro inicializador, escriba un guión bajo (_) en lugar de una etiqueta de argumento explícito para ese parámetro para anular el comportamiento predeterminado.

Ahora, agreguemos un método init a la clase Humana como este:

init (_ edad: Int, _ género: género) {
self.age = age
self.gender = género
}

Ahora podemos llamar:

let human5 = Human (40, .male)

Inicializadores predeterminados

Swift proporciona un inicializador predeterminado para cualquier estructura o clase que proporciona valores predeterminados para todas sus propiedades y no proporciona al menos un inicializador. El inicializador predeterminado simplemente crea una nueva instancia con todas sus propiedades establecidas en sus valores predeterminados.

Este ejemplo define una clase llamada ShoppingListItem, que encapsula el nombre, la cantidad y el estado de compra de un artículo en una lista de compras:

clase ShoppingListItem {
nombre var: cadena?
cantidad var = 1
var comprado = falso
}
artículo var = ShoppingListItem ()

Inicializadores de Memberwise para tipos de estructura

Los tipos de estructura reciben automáticamente un inicializador miembro si no definen ninguno de sus propios inicializadores personalizados.

Puntos a tener en cuenta:

  • Si no proporcionamos un inicializador personalizado, la estructura recibe un inicializador miembro, incluso si tiene propiedades almacenadas que no tienen valores predeterminados.
  • Si no proporcionamos un inicializador personalizado, la estructura recibe un inicializador miembro, incluso si tiene propiedades almacenadas que tienen valores predeterminados.

Considere una estructura llamada Tamaño

Tamaño de estructura {
var ancho, alto: doble // propiedades almacenadas sin valores predeterminados
}
-------- o --------------
Tamaño de estructura {
var ancho = 10.0, altura = 30.0 // propiedades almacenadas con valores predeterminados
}

La primera estructura tiene dos propiedades almacenadas sin valores predeterminados. La segunda estructura tiene dos propiedades almacenadas con valores predeterminados dados. Los inicializadores personalizados no se dan en ambos casos. Para inicializar esta estructura, obtenemos un inicializador miembro con ancho y alto como parámetros:

let twoByTwo = Size (ancho: 2.0, alto: 2.0)
Si proporcionamos un método de inicio personalizado, todas las propiedades almacenadas deben inicializarse como se explicó anteriormente en este artículo. Además, el inicializador miembro no será accesible en este caso. es decir; si define un inicializador personalizado para un tipo de valor, ya no tendrá acceso al inicializador predeterminado (o al inicializador miembro, si es una estructura) para ese tipo.
Tamaño de estructura {
var ancho, altura: doble
en eso(){
self.width = 10.0
self.height = 30.0
}
}
let sizeObj1 = Tamaño (ancho: 2.0, alto: 2.0) // error. argumento pasado a la llamada que no toma argumentos
let sizeObj2 = Size () // éxito.

Delegación de inicializador para tipos de valor

Los inicializadores pueden llamar a otros inicializadores para realizar parte de la inicialización de una instancia. Este proceso, conocido como delegación de inicializador.

  • Los tipos de valor (estructuras y enumeraciones) no admiten la herencia, por lo que su proceso de delegación de inicializador es relativamente simple, ya que solo pueden delegar a otro inicializador que se proporcionan.
  • Clases, pueden heredar de otras clases, como se describe en Herencia. Esto significa que las clases tienen responsabilidades adicionales para garantizar que a todas las propiedades almacenadas que heredan se les asigne un valor adecuado durante la inicialización.

Ejemplo:

El siguiente ejemplo define una estructura Rect personalizada para representar un rectángulo geométrico. El ejemplo requiere dos estructuras de soporte llamadas Tamaño y Punto, que proporcionan valores predeterminados de 0.0 para todas sus propiedades:

Tamaño de estructura {
var ancho = 0.0, altura = 0.0
}
punto de estructura {
var x = 0.0, y = 0.0
}

Puede inicializar la estructura Rect a continuación en una de tres formas: mediante el uso de sus valores de propiedad de tamaño y origen inicializados por cero predeterminados, proporcionando un punto y tamaño de origen específicos, o proporcionando un punto central y tamaño específicos. Estas opciones de inicialización están representadas por tres inicializadores personalizados que forman parte de la definición de Rectstructure:

struct Rect {
origen var = Punto ()
var size = Size ()
en eso() {}
init (origen: punto, tamaño: tamaño) {
self.origin = origen
self.size = size
}
init (centro: Punto, tamaño: Tamaño) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init (origin: Point (x: originX, y: originY), size: size)
}
}

Inicializadores designados e inicializadores de conveniencia

A todas las propiedades almacenadas de una clase, incluidas las propiedades que la clase hereda de su superclase, se les debe asignar un valor inicial durante la inicialización.

Swift define dos tipos de inicializadores para los tipos de clase para ayudar a garantizar que todas las propiedades almacenadas reciban un valor inicial.

  • Inicializadores designados
  • Inicializadores de conveniencia

Los inicializadores designados son los inicializadores principales para una clase. Un inicializador designado inicializa completamente todas las propiedades introducidas por esa clase y llama a un inicializador de superclase apropiado para continuar el proceso de inicialización en la cadena de superclase.

Cada clase debe tener al menos un inicializador designado.

Los inicializadores designados para las clases se escriben de la misma manera que los inicializadores simples para los tipos de valor:

init (_parameters if any_) {
}

Los inicializadores de conveniencia son secundarios y admiten inicializadores para una clase. Puede definir un inicializador de conveniencia para llamar a un inicializador designado de la misma clase que el inicializador de conveniencia con algunos de los parámetros del inicializador designado establecidos en los valores predeterminados. También puede definir un inicializador de conveniencia para crear una instancia de esa clase para un caso de uso específico o tipo de valor de entrada.

No tiene que proporcionar inicializadores de conveniencia si su clase no los requiere. Cree inicializadores convenientes siempre que un acceso directo a un patrón de inicialización común ahorre tiempo o haga que la inicialización de la clase sea más clara en su intención.

Los inicializadores de conveniencia están escritos en el mismo estilo, pero con el modificador de conveniencia colocado antes de la palabra clave init, separados por un espacio:

conveniencia init (_parameters if any_) {
}

Veamos un ejemplo: la clase humana tiene un método init designado y un método init conveniente.

clase HumanBeing {
nombre var: cadena
init (nombre: Cadena) {
self.name = name
}
conveniencia init () {
self.init (nombre: "no establecido")
// Conveniencia init llama al método init designado
}
}
let humanBeingObj1 = HumanBeing () // llama a init de conveniencia
let humanBeingObj2 = HumanBeing (nombre: "abhilash") // llama a init designado
Convenience init inicializa el método init designado llamando a self.init.

Delegación de inicializador para tipos de clase

Para simplificar las relaciones entre los inicializadores designados y convenientes, Swift aplica las siguientes tres reglas para las llamadas de delegación entre inicializadores:

  • Un inicializador designado debe llamar a un inicializador designado desde su superclase inmediata. Un inicializador designado de una subclase no puede llamar a un Convenience init desde su superclase. Si intenta hacerlo, recibirá un error que dice "error: debe llamar a un inicializador designado de la superclase".
  • Un inicializador de conveniencia debe llamar a otro inicializador de la misma clase.
  • Un inicializador de conveniencia finalmente debe llamar a un inicializador designado.

Eche un vistazo a la siguiente imagen para que quede más clara:

fuente: documentos de apple

Herencia de inicializador automático

Las subclases no heredan sus inicializadores de superclase de forma predeterminada; sin embargo, los inicializadores de superclase se heredan automáticamente si se cumplen ciertas condiciones.

Si proporcionamos valores predeterminados para cualquier propiedad nueva que introduzca en una subclase, se aplicarán las siguientes dos reglas:

  • 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.

Considere el siguiente ejemplo y los métodos de inicio disponibles para crear un objeto de clase Man.

clase HumanBeing {
nombre var: cadena
init (nombre: Cadena) {
self.name = name
}
conveniencia init () {
self.init (nombre: "no establecido")
// Conveniencia init llama al método init designado
}
}
let humanBeingObj1 = HumanBeing () // llama a init de conveniencia
let humanBeingObj2 = HumanBeing (nombre: "abhilash") // llamadas
init designado
____________________
Clase hombre: HumanBeing {
var age: Int = 0
anular init (nombre: cadena) {
super.init (nombre: nombre)
}
init (nombre: Cadena, edad: Int) {
super.init (nombre: nombre)
self.name = name
self.age = age
}
}
_______________________
let manObj1 = Man () // llama a la conveniencia init de la clase Humana
let manObj2 = Man (nombre: "Robert") // llama a init anulado
let manObj3 = Man (nombre: "John", edad: 10) // llama a init personalizado
manObj1.name // imprime "no establecido"
manObj2.name // imprime "Robert"
manObj3.name // imprime "John"

A dónde ir desde aquí:

Inicializadores en swift parte-2: Inicializadores fallidos en swift

Si disfrutaste leyendo esta publicación y la encontraste útil, compártela y recomiéndala para que otros puedan encontrarla.

¡¡Gracias!!