VISITAS:

domingo, 9 de agosto de 2015

SWIFT (II) TIPOS OPTIONAL

Swift es un lenguaje de programación moderno que utiliza las mejores técnicas de los lenguajes de programación que han tenido más éxito en los últimos años: Java, Objective-C, Python, etc.
Este nuevo lenguaje de programación "inventado" por Apple para que sea su lenguaje del futuro en plataformas iOS y OSX tiene muchísimas ventajas, porque ha cogido lo mejor de cada lenguaje, convirtiéndose hoy en día (2015) en el lenguaje más seguro y potente que existe.
Sin embargo, hay ciertas características de swift que no han sido "copiadas" de otros lenguajes y que suponen una barrera de entrada al lenguaje, incluso para programadores muy experimentados. Una de esas barreras son los tipos optional, algo que bloquea a muchos programadores cuando empiezan con swift (conozco a algún programador que empezó con mucha ilusión a aprender swift y cuando se encontró con los tipos optional, después de dar muchas vueltas al tema, no consiguió entenderlo y abandonó el lenguaje "para otro momento").
Voy a intentar explicar (en español y con ejemplos) los tipos optional de swift para que no sean una barrera para nadie. Supongo que se me quedarán algunas cosas en el tintero, y también supongo que cuando el lector pase por este blog, el lenguaje swift habrá cambiado bastante y que algunas de las cosas que aquí expongo podrían haber cambiado (prometo mantener esta entrada al día en la medida de lo posible). 

TIPOS OPTIONAL

Los lenguajes de programación permiten expresar la situación en la que una variable no contiene ningún valor. Esto es muy útil, por ejemplo, cuando queremos retornar nil (o null) desde una función indicando que no se retorna nada. También es útil para indicar que una variable todavía no se ha inicializado. En lenguajes como C++ o Java, para indicar que una variable no contiene ningún valor, se asigna el valor nil a la variable. Esta solución funciona para variables de tipo objeto, pero no para los tipos básicos: int, double, boolean, etc. En estos casos teníamos que asignar valores especiales para indicar que la variable no tiene valor, por ejemplo -1. Swift resuelve este problema de manera general para todos los tipos mediante los tipos optional.

En Swift se definen los tipos optional añadiendo el carácter ? al final del tipo. Ejemplos:

        Int?
        String?
        MiClase?
        etc

En Swift, una variable de un tipo optional puede contener un valor (básico u objeto, según el tipo) o bien no contener ningún valor. Sólo se puede asignar nil a una variable de un tipo optional. A una variable que no sea optional (sin el carácter ?), no se le puede asignar nil, ya que el compilador daría error:

        var i : Int? = nil
        var j : Int = nil   --> error de compilación

Igualmente si una variable es de tipo optional, no podemos asignarla a otra variable que no sea optional (incluso aunque contenga un valor).

        var k : Int? = 7
        var m : Int = k   --> error de compilación

Tenemos que pensar que los tipos Int e Int? no son lo mismo. El tipo Int? es un wrapper de Int, es decir, una especie de objeto que contiene un Int, por tanto, no podemos asignar un Int? a un Int. Sin embargo, sí es posible asignar un Int a un Int?, ya que el compilador hace las comversiones adecuadas. En el siguiente apartado veremos cómo asignar una variable Int? a otra de tipo Int.

En resumen, cualquier tipo Swift puede ser optional, tanto los objetos como los tipos básicos. Y sólo podremos asignar nil a una variable de un tipo optional.

FORCED UNWRAPPING

Forced unwrapping es el mecanismo que proporciona swift para asignar una variable de un tipo optional a una variable no optional. Forced unwrapping permite obtener la variable interna (si existe) que tiene el wrapper optional. Por ejemplo:

        var a : Int? = 5
        var b : Int = a!

Añadiendo el carácter ! al final de la variable se hace un forced unwrapping de la variable optional, es decir, se extrae el valor de la variable si existe (si la variable no contuviera un valor, o sea nil, se produciría una excepción en runtime).

Cuando se va a hacer forced unwrapping Para comprobar si el valor de una variable es conveniente comprobar primero si la variable optional es distinta de nil, es decir, si tiene un valor. Para ello se compara utilizando los operadores == y != (habituales en lenguajes de programación tipo C). Si hemos comprobado que una variable es != nil porque hemos hecho un if previamente, entonces podríamos leer su valor con tranquilidad:

        if a != nil {
                b = a!
                print("Se puede leer el valor de a = \(a!) sin problemas")
        } else {
                print("La variable a no contiene ningún valor")
        }

OPTIONAL BINDING

Optional binding es una técnica de swift que permite, al mismo tiempo que se comprueba que una variable optional tiene un valor, asignar ese valor a otra variable (o constante) para utilizarla después sin necesidad de forced unwrapping.

La sintaxis del optional binding es:

        var a : Int? = 4
        if let b = a {
                print("Se puede leer el valor de b = \(b) sin problemas")
        } else {
                print("La variable a no contiene ningún valor")
        }

Se pueden utilizar constantes (let) o variables (var) en optional binding, dependiendo de si el valor se va a modificar después o no.

También se pueden crear varias variables y constantes en un optional binding:

        if let a1 = a, b1 = b {

IMPLICIT UNWRAPPED OPTIONALS

Algunas veces, tenemos claro que una variable optional va a tener siempre un valor (es decir, el programador sabe que esa variable siempre va a contener un valor). En estos casos puede ser útil indicar en la declaración este hecho. Para ello, se utiliza la siguiente sintaxis:

        var i : Int!  --> se inicializa a nil
        i = 5

Este tipo de optionals se llaman implicit unwrapped optionals, indicando que la variable es optional pero el compilador va a hacer un unwrapped de forma implícita cada vez que se utilice. Por ejemplo:

        var j = i   --> no hace falta forced unwrapping

Lo primero que nos preguntamos tras leer estas líneas es: Y entonces, ¿por qué no declaramos la variable como no optional? Es la pregunta obvia después de la definición de implicit unwrapped optional. Veamos un caso útil en un constructor de una clase:

        class Ciudad {
                var nombre : String!    --> se inicializa a nil, por tanto tiene que ser optional
                init( nombreCiudad:String) {
                        self.nombre = nombreCiudad   --> se le asigna un valor y ya nunca va a ser nil
                }
        }

A partir de ese momento, la variable city.nombre se puede utilizar normalmente, sin hacer forced unwrapping. Es como si dejáramos en la responsabilidad del programador asegurar que una variable nunca va a ser nil (aunque para el compilador, sí podemos asignarle un valor nil).

Alguien podría decir ahora: "Yo tengo una solución más sencilla a este caso"

        class Ciudad {
                var nombre : String = ""  --> no tiene porqué ser optional
                init( nombreCiudad:String) {
                        self.nombre = nombreCiudad
                }
        }

Y tendría razón, así resolvemos también el problema. Pero podríamos complicar el caso un poco más y la solución ya no sería tan sencilla:

        class Ciudad {
                var elAlcalde : Alcalde!    --> se inicializa a nil
                init( nombreAlcalde:String) {
                        self.elAlcalde = Alcalde(nombreAlcalde)
                }
        }

Una variable implicit unwrapped optional es realmente una variable optional (por debajo) pero se puede utilizar como una variable no optional sin necesidad de hacer unwrapping cada vez que se usa. Esta es la diferencia:

        let a : String? = "Hola"
        var a1 = a!   --> es necesario forced unwrapping

        let b : String! = "Adiós"
        var b1 = b  --> se accede normalmente, a pesar de que b es optional

Con las variables implicit unwrapped optional estamos dando permiso al compilador para que haga un unwrap automáticamente cada vez que se utiliza. Es nuestra responsabilidad asegurar que la variable contiene un valor (si no lo tuviera, se elevaría una excepción de runtime).

Es posible también hacer un chequeo del valor de la variable cuando no estemos seguros:

        var a : String! = "hola"
        if a != nil {
                var b = a
        }

Yo creo que las variables implicit unwrapped optional son muy similares a las variables objeto de otros lenguajes, donde es nuestra responsabilidad comprobar previamente si tienen un valor, y si no lo tienen, se eleva una excepción.

CONCLUSIÓN

Apple recomienda utilizar variables optional ? cuando no estemos seguros de que la variable siempre va a contener un valor. Si estamos seguros de que siempre va a contener un valor, usemos implicit unwrapped optional !. Y si se inicializa con un valor y siempre va a tenerlo, entonces usaremos variables no optional. Éste sería un resumen final de todo este capítulo dedicado al novedoso tema de las variables optional en swift.

Si tenéis comentarios, sugerencias, habéis visto errores, por favor, no dudéis en poner un comentario en esta entrada de blog.











3 comentarios:

  1. Woow! Genial tu explicación. Yo estaba viendo unos tutoriales en inglés, pero no logré entender esas sutilezas. Se agradece todo el trabajo

    ResponderEliminar
  2. Excelente explicación. Solamente una duda...

    En la siguiente línea
    var elAlcalde : Alcalde! --> se inicializa a nil

    Se están declarando dos variables (elAlcalde y Alcalde!) al mismo tiempo??

    ResponderEliminar
  3. ros de que siempre va a contener un valor, usemos implicit unwrapped optional !. Y si se inicializa con un valor y siempre va a tenerlo, entonces usaremos variables no optional. Éste sería un resumen final de todo este capítulo dedicado al novedoso tema de las variables optional en swift. https://coaching-mastery.com/5-formas-para-ahorrar-dinero-rapido/

    ResponderEliminar