ProgramaciónDesarrollador iOS Senior

¿Cómo se implementa la inicialización de propiedades en Swift? ¿Cuáles son las diferencias entre los inicializadores designated, convenience y required? ¿Qué matices surgen al trabajar con jerarquías de inicialización?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Swift, todas las propiedades de tipos no opcionals deben ser inicializadas antes de que finalice la inicialización de la instancia. Para esto se utilizan inicializadores: designated (principales), convenience (auxiliares) y required (obligatorios para los herederos).

  • Designated inicializador: es el principal, se encarga de inicializar todas las propiedades de la clase y de llamar al inicializador designated de la superclase. Cada clase tiene al menos un inicializador designated.
  • Convenience inicializadores: son auxiliares, delegan la inicialización a la misma clase (llaman a otros inicializadores dentro de la clase a través de self.init(...)), simplificando la creación del objeto con diferentes conjuntos de parámetros.
  • Required inicializador: requiere que todas las subclases implementen este inicializador. Se declara con el modificador required.

Ejemplo:

class Vehicle { let numberOfWheels: Int required init(numberOfWheels: Int) { self.numberOfWheels = numberOfWheels } } class Car: Vehicle { let brand: String // Inicializador designated required init(numberOfWheels: Int) { self.brand = "Desconocido" super.init(numberOfWheels: numberOfWheels) } // Convenience convenience init() { self.init(numberOfWheels: 4) self.brand = "Ford" } }

Características clave:

  • Todas las propiedades deben ser inicializadas antes de acceder a super.init.
  • Los inicializadores convenience siempre deben llamar a un inicializador designated o a otro inicializador convenience self.
  • Los inicializadores required deben ser implementados en todos los herederos.

Pregunta trampa.

¿En qué orden se deben inicializar las propiedades de la subclase y la superclase al usar un inicializador designated?

Respuesta: Todas las propiedades de la subclase deben ser inicializadas antes de llamar al inicializador designated de la superclase (super.init), de lo contrario habrá un error de compilación. Después de super.init no se pueden inicializar nuevas propiedades almacenadas de la subclase, solo se pueden utilizar.

class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // Primero sus propiedades super.init(prop: prop) // Luego parent } }

Ejemplos de errores reales debido al desconocimiento de los matices del tema.


Historia

Un desarrollador en un inicializador convenience intentó asignar directamente un valor a una propiedad required, sin llamar a designated. Como resultado, la propiedad fue inicializada dos veces con diferentes valores, lo que llevó a un comportamiento inesperado durante las pruebas.


Historia

Al heredar una jerarquía de clases profunda, olvidaron declarar required init, lo que condujo a la imposibilidad de inicializar correctamente un tipo derivado desde un marco a través de reflection, y a un crash al deserializar un modelo JSON.


Historia

En una aplicación, durante la extensión de la clase Vehicle, no se llamó al inicializador designated de la superclase, lo que llevó a una inicialización incorrecta de las propiedades obligatorias y a un crash en producción después de la actualización del esquema de datos.