El otro día un amigo me comentaba que estaba implementando un juego para probar Swift por primera vez y me contaba que estaba teniendo problemas entendiendo algunas cosas de los opcionales (optionals) en Swift. Pensé que era un buen tema para escribir un pequeño post y de paso generar un poco de documentación en Español, lo cual es uno de los principales propósitos de este blog.
Opcionales
Según la documentación de Swift,
Los opcionales se utilizan en situaciones donde el valor puede estar ausente.
Este concepto no existe en Objective-C, en el cual es permitido incluso enviar un mensaje a nil. Lo más cercano a representar ausencia de valor es retornando nil en métodos que de otra manera deberían devolver un objeto, pero esto solo funciona con objetos y no con enumeraciones, estructuras y tipos básicos de C para los cuales generalmente se utiliza NSNotFound para representar ausencia de valor; en dependencia del tipo, algunos desarrolladores también utilizan 0, -1, NSIntegerMax y cosas parecidas.
De cierto modo Swift viene a unificar la representacion de ausencia de valor con los opcionales.
En Swift cuando declaramos variables, estas por defecto son “no opcionales”, por lo que tienes que asignarles valores distintos de nil, si intentas asignarles nil obtendrás un error en tiempo de complicación. Si pruebas el siguiente código en Playground verás el error “Nil cannot be assigned to type ‘String'” en la segunad línea, esto también se aplica a las propiedades de una clase:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var saludo:String = "Hola a todos" | |
saludo = nil |
Para hacer que esta variable sea opcional y podamos asignarle nil solo tenemos que colocar el signo de interrogación ? al final del tipo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var saludo:String? = "Hola a todos" | |
saludo = nil |
Algo importante es saber cómo “leer” la declaración de un opcional, de la declaración de la variable saludo en el ejemplo anterior podemos leer:
“saludo es un opcional, el cual puede o no contener un valor de tipo String“
Esto es importante comprenderlo porque saludo NO ES un String sino un opcional, el cual es un tipo de datos en Swift.
Usos
Aunque ya vimos cómo declarar un opcional veamos algunos ejemplos de uso.
Cuando declaramos un opcional y no le asignamos un valor, su valor por defecto será nil, en el siguiente ejemplo, codigoRespuestaServidor tiene el valor nil:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var codigoRespuestaServidor:Int? |
Podemos comprobar si un optional tiene o no un valor comparando utilizando la sentencia if para comparar con nil:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var codigoRespuestaServidor:Int? = 404 | |
if codigoRespuestaServidor != nil { | |
print("codigoRespuestaServidor contiene un código") | |
} |
Si ejecutamos el código anterior en Playground veremos que se imprime el valor “codigoRespuestaServidor contiene un código\n”.
Luego de realizar este chequeo podemos con seguridad obtener el valor que contiene el opcional utilizando el operador signo de exclamación ! como vemos en el siguiente ejemplo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var codigoRespuestaServidor:Int? = 404 | |
if codigoRespuestaServidor != nil { | |
print("codigoRespuestaServidor contiene el código \(codigoRespuestaServidor!)") | |
} |
A esta operación se le conoce como desenvolver forzosamente (forced unwrapping) el valor del opcional.
Estas dos operaciones, chequear si el opcional contiene un valor y desenvolver el valor, pueden ser combinadas en una operación conocida como unión opcional (optional binding) la cual puede ser utilizada en sentencias if y while, por ejemplo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if let numero = Int("5") { | |
print("El número es \(numero)") | |
} else { | |
print("No se puede convertir a número") | |
} |
En el ejemplo anterior suceden varias cosas, primeramente utilizamos un inicializador de del tipo Int que recibe como argumento un String, el cual trata de convertir a Int; este inicializador está propenso a fallar porque no todos los valores String pueden ser convertidos a Int, por ejemplo “no soy un número”, por lo tanto, este devuelve un opcional Int? y no un Int directamente.
Entonces, regresando al ejemplo, la unión opcional ocurre en la línea 1, donde se ejecuta el inicializador de Int que convierte un String a Int, el cual devuelve un opcional Int?, chequea que dicho opcional contiene un valor y de ser así lo asigna a la constante numero. Como la unión ocurrió satisfactoriamente, entonces se ejecuta la primera parte del if y se imprime “El número es 5\n”.
Si en lugar de “5” pasamos un valor String que no represente a un número entero, como ya dijimos “no soy un número”, se ejecutará el código del else imprimiendo “No se puede convertir a número\n”.
Otro concepto asociado a los opcionales es el de opcionales desenvuelto implícitamente (implicitly unwrapped optionals), estos son útiles cuando está confirmado que el valor del opcional va a existir inmediatamente luego de ser definido y puede asumirse que va existir siempre a partir de ese momento; es como darle permiso al opcional a ser desenvuelto cada vez que se utilice, para ello en lugar de colocar el signo de exclamación detrás del nombre del opcional, se coloca detrás de su tipo luego de declararlo, veamos dos ejemplo:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ejemplo 1 | |
let pais:String! | |
pais = "Cuba" | |
print("El país es \(pais)") | |
// ejemplo 2 | |
let cantidad:Int! = 4 | |
print("La cantidad es \(cantidad)") |
Podemos ver en el primer ejemplo como no necesitamos utilizar el signo de exclamación luego de la constante pais en la línea 3
Podemos tratar este tipo de opcionales como mismo tratamos un opcional normal, podemos chequear si contiene un valor, utilizar unión opcional; si el valor de un opcional desenvuelto implícitamente es nil y tratamos de acceder a su valor, esto provocará un error en tiempo de ejecución.
El post no abarca a fondo los opcionales pero espero que ayude por lo menos a tener una idea de qué son y como utilizarlos.