Valores Opcionales

Si Swift tiene algo que llama la atención a primera vista, son los signos de interrogación y exclamación que se llegan a ver en el código de vez en vez.

let nameAnswer: FormAnswer
let phoneNumberAnswer: FormAnswer?

A cualquier tipo de dato que sea seguido de un signo de interrogación se le conoce como un valor opcional (Optional).

Cuando un valor o tipo de dato está seguido por un signo de interrogación, se dice que ese valor o tipo de dato es “Opcional” (Optional).

Que algo sea opcional, como te puedes imaginar, significa que el valor puede o no existir. Es como si se dijera: “tal vez tenga un string dentro, o tal vez no.”

El ejemplo anterior vagamente hace referencia a una encuesta de usuario, donde existen dos campos que el usuario tiene que llenar.

  1. nameAnswer almacenará la respuesta que el usuario de para el campo “Nombre”.
  2. phoneNumberAnswer hará lo mismo para el campo “Número de teléfono”.

La única diferencia, es que el campo “Nombre” siempre tiene que ser llenado, pero el campo “Número de teléfono” es opcional. phoneNumberAnswer puede o no contener una respuesta cuando el usuario finalice el llenado del formulario, por lo tanto se declara como “FormAnswer opcional”, o FormAnswer?.

El propósito general de los Opcionales en Swift es proveer un mecanismo a través del cual el programador pueda hacer decisiones informadas sobre la ejecución de algún comando.

Al momento de querer declarar un tipo de dato opcional, en este ejemplo un número entero, Int? y Optional<Int> son exactamente lo mismo.

let optionalNumberA: Int?
let optionalNumberB: Optional<Int>

Aunque ambas formas de declarar un valor opcional son correctas, la notación abreviada (usando ? luego del tipo de dato) es preferida.

nil

Se puede pensar en los valores opcionales como como contenedores especiales. Int por si solo representa un número entero, pero Int? representa una caja que puede o no tener un número entero dentro.

Cuando no existe valor dentro de un opcional, se dice que ese opcional es nil. nil representa la ausencia de un valor de un tipo concreto, independientemente del tipo de dato base.

var accessCode: String? = nil

print(accessCode)		// #=> nil

accessCode = "12345"

print(accessCode)		// #=> "12345"

accessCode = nil

print(accessCode)		// #=> nil

type(of: accessCode)	// #=> Optional<String>.Type

Verificando la existencia de un valor en un opcional

Al hecho de obtener el valor dentro de un Opcional se le llama optional unwrapping, o “desempaquetado del opcional.” Se sigue usando la analogía de que un Opcional es una caja que puede o no tener un valor dentro.

Hay dos formas de hacer optional unwrapping: forzada y segura.

Forma controlada

Para desempaquetar un opcional de forma segura, usamos lo que se le conoce como “enlazado de opcionales”, y “optional binding” en inglés.

Ejemplo usando la forma if let:


let optionalGreeting: String? = "Hello, world!"

if let greeting = optionalGreeting {
	// optionalGreeting si contenía un valor dentro del opcional,
	// el cual es asignado a la constante greeting.
	// greeting solamente puede ser usado dentro del bloque if.

	print(greeting) 
} else {	
	// optionalGreeting no contenía ningún valor dentro del opcional,
	// lo que quiere decir que es nil.
}

Ejemplo usando valores por defecto:

// numberOfPizzas podría ser un valor proporcionado por el usuario
let numberOfPizzas: Int?

// La función orderPizzas(number:) ordenará tantas pizzas 
// como le sean pasadas en el parámetro number
func orderPizzas(number: Int) {} 

// Si numberOfPizzas contiene un valor, es decir, no es nil, usa ese valor.
// De lo contrario, si numberOfPizzas no tiene valor, es decir, es nil, usa el valor
// por defecto, en este caso 10.
orderPizzas(numberOfPizzas ?? 10)

Si solamente se requiere verificar que un opcional sea o no nil, se puede hacer directamente en la directiva if/else sin necesidad de desempaquetar el opcional:

if userInput != nil {
	// userInput no es nil, es decir, sí contiene un valor dentro del opcional
	// pero dicho valor no se ha desempaquetado.
}

Forma forzada

Cuando se quiera acceder a un valor dentro de un opcional sin primero verificar la existencia del mismo, se puede forzar el desempaquetado, en inglés conocido como force unwrapping.

Hacer desempaquetado de forma forzada debería de considerarse un último recurso cuando se trabaje con opcionales.

Si se desempaqueta un opcional de forma forzada cuando el opcional sea nil se producirá un error de memoria y la ejecución del programa se detendrá.

let phoneNumberAnswer: FormAnswer?

// Usando ! desempaquetamos forzadamente el opcional phoneNumberAnswer
// pero si este es nil, se interrumpirá la ejecución del programa.
print(phoneNumberAnswer!)

Relacionados