Inicializadores Falibles

Tutorial Swift – Inicializadores Falibles

Josué V. Herrera Swift Avanzado, Tutoriales 0 Comments

En este Tutorial Swift, abordaremos los Inicializadores Falibles, un ejemplo más de cuan flexible es Swift como lenguaje, un tema que nos ayudará a mejorar nuestros diseños, ya que como veremos más adelante, en ocasiones es beneficioso definir inicializadores que puedan fallar. Este fallo puede ser desencadenado o emitido por una inicialización inválida de alguna propiedad, la ausencia de un recurso externo requerido o alguna otra condición que impida la correcta inicialización de esa clase, estructura o enumeración.

Para hacer frente a condiciones de inicialización que puedan fallar, necesitamos definir uno o más inicializadores falibles. Esto lo logramos mediante la colocación de un signo de interrogación (?) después de la palabra clave init. Dentro del inicializador también escribimos return nil para indicar que ha fallado la inicialización.

Cabe recalcar los siguientes puntos:

  • El inicializador falible creará un valor opcional del tipo que se está inicializando es decir la instancia que se crea es del mismo tipo de la clase pero opcional.
  • No se puede definir un inicializador falible con el mismo nombre y parámetros que uno no-falible (estándar).
  • Estrictamente hablando un inicializador no retorna ningún valor, mas bien, su función es asegurar que self o la nueva instancia como tal, sea inicializada completamente para el momento en que el inicializador termine. En el caso del inicializador falible aunque escribimos return nil para notificar que ha ocurrido un fallo, no usamos la palabra clave return para indicar que todo ha salido bien, por ende es algo solamente enfocado para con los inicializadores falibles.

El ejemplo a continuación define una estructura llamada Animal, con una constante String llamada species. En esta estructura también declaramos un inicializador falible de un solo parámetro. Este inicializador chequea si el valor pasado a species no es una cadena vacía. En caso de que la cadena vacía sea pasada al inicializador pues desencadena una fallo de inicialización , de lo contrario el valor del parámetro es almacenado y la inicialización terminan satisfactoriamente.

…en este ejemplo hemos creado una instancia de Animal pasando como parámetro una Girafa luego mediante un if let verificamos que la instancia no sea nil, es decir que se haya inicializado satisfactoriamente. Recordemos que cuando llamamos a un inicializador falible la instancia creada es opcional, por esto es que hacemos el chequeo de esta manera.

Inicializadores Falibles en Enumeraciones

En las enumeraciones podemos hacer uso de los inicializadores falibles para seleccionar el caso apropiado basándonos en uno o más parámetros. Es decir que podemos emitir un fallo si el parámetro no concuerda con un caso en específico. Veamos un ejemplo:

…hemos declarado una enumeración llamada TemperatureUnit con tres posibles estados (Kelvin, Celsius y Fahrenheit). En un inicializador falible lo usamos en pos de encontrar el caso apropiado para el valor que representa el símbolo de la temperatura.

Propagación de un Inicializador Falible

Un inicializador falible de una clase, estructura o enumeración puede delegar hacia otro inicializador falible del mismo tipo, es decir declarado en la misma clase, estructura o enumeración. De manera similar el inicializador falible de una subclase puede delegar hacia el inicializador falible de una super-clase.

Veamos un ejemplo:

En este ejemplo hemos creado una sub-clase de Product llamada CartItem. La clase CartItem representa un artículo en un carro de compra online. CartItem también introduce una propiedad constante llamada quantity y se encarga de que esta siempre tenga un valor de al menos 1. En caso de que la cantidad (quantity) sea inválida el proceso de inicialización completo falla inmediatamente y ningún otro código restante es ejecutado. Del mismo modo el inicializador falible de Product verifica el valor de name y en caso de que sea una cadena en blanco este falla inmediatamente.

Si creamos una instancia de CartItem con un nombre y una cantidad de uno o más, la inicialización termina satisfactoriamente:

…la salida en pantalla sería:

Ahora, si tratamos de crear una instancia de CartItem con una cantidad de 0, el inicializador fallará

…la salida en pantalla sería:

De manera similar si declaramos nuevamente una instancia de CartItem con el valor name vacío, el inicializador falible de la clase padre Product detendrá el proceso de inicialización:

…la salida en pantalla sería:

Sobreescritura de Inicializadores Falibles

Podemos sobreescribir el inicializador falible de una super clase, tal y como hacemos con cualquier otro inicializador. También podemos alternativamente sobreescribir el inicializador falible de una super clase con un inicializador no-falible de una clase hija. Esto nos permite definir una clase hija por la cual el inicializador no puede fallar, incluso si la inicialización de la super clase tiene previsto un posible fallo.

Hay que hacer notar que podemos sobreescribir un inicializador falible con uno no-falible pero no al revés.

El ejemplo a continuación define una clase llamada Document. Esta clase modela un documento que puede ser inicializado con una propiedad llamada name la cual puede almacenar una cadena de texto o nil, pero no una cadena en blanco:

Ahora veamos la clase AutomaticallyNamedDocument que adopta a la clase Document como su clase padre o super clase:

…esta clase sobreescribe ambos de los inicializadores designados introducidos por Document. Esta sobreescritura se asegura de que la propiedad name siempre tendrá como valor inicial “[Untitled]” en caso de que la instancia de AutomaticallyNamedDocument sea inicializada sin un nombre o se le pase una cadena en blanco.

Como podemos comprobar hemos sobreescrito el inicializador falible init?(name:) de la clase padre con un inicializador no-falible init(name:). Esto lo hemos hecho ya que AutomaticallyNamedDocument maneja de una manera distinta la posibilidad de que la propiedad name contenga una cadena de texto vacía, por lo cual no necesita fallar, no necesita interrumpir el proceso de inicialización en caso de que esto ocurra.

El Inicializador Falible init!

Como hemos visto hasta ahora, cuando declaramos un inicializador falible (init?) obtenemos una instancia opcional,  siempre en caso de que el inicializador termine satisfatoriamente su ejecución. Alternativamente podemos definir un inicializador falible el cual cree una instancia opcional implícitamente unwrapped (desenvuelta).  Esto lo podemos lograr colocando un símbolo de exclamación luego de la palabra clave init, es decir colocaríamos el signo ! en lugar de ?, quedando init!.

También podemos delegar desde un init? hacia un init! y viceversa, de igual manera podemos sobreescribir init? con un init! y viceversa.

Inicializadores Requeridos

Cuando declaramos un inicializador con la palabra clave required antes de init estamos indicando que toda clase que herede de esta tiene que implementar el inicializador:

…como vemos también podemos escribir la palabra required en el inicializador de la clase hija para mantener este requerimiento a lo largo de la cadena. Al mismo tiempo que no es necesario usar la palabra clave override ya que el comportamiento es claramente inferido por el compilador.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!