Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

Swift y los nulos

Swif-for-iOSLa gestión de las referencias (o punteros) nulas es uno de los problemas clásicos cuando se desarrolla con un lenguaje orientado a objetos. Acceder a una referencia nula genera un error de ejecución que además tiene difícil tratamiento. Incluso aunque el lenguaje te permita capturar dicho error (como Java o C#) la verdad es que uno, como desarrollador, poco puede hacer ante él: si se intenta acceder a un objeto pero resulta que no existe dicho objeto poca cosa puede hacerse. La mayoría de los desarrolladores asumen que las referencias pueden ser nulas y lo ven como algo normal. Pero realmente eso es una mala idea. No hay motivo alguno por el cual todas las referencias deban poder ser nulas (es decir no apuntar a ningún objeto). El único motivo real es que la mayoría de los lenguajes admiten dicha posibilidad.

En Swift las cosas son ligeramente distintas, y si vienes de Objective-C, C++, C# o Java entre otros, te puedes llevar más de una sorpresa. Por defecto en Swift las referencias no toman el valor nil (el valor nil representa una referencia nula). Si por ejemplo definimos la siguiente clase:

class Ufo {
     init() {}
     var name  = ""
}

Podemos declarar una referencia que contenga un objeto de tipo Ufo de la siguiente manera:

var u =  Ufo()

Pero si luego intentamos asignar el valor nil a dicha referencia se obtendrá un error (Type does not conform to protocol NilLiteralConvertible).

swift-nulos1

 

Podemos declarar la variable sin inicializarla, pero Swift nos obligará a asignarle un valor antes de poder usarla. Supongamos una función UfoName que devuelva el nombre de un objeto Ufo:

func UfoName (u:Ufo) -> String {
     return u.name
}

El siguiente código da el error Variable u used before being initialized:

var u:Ufo
UfoName(u)

Referencias opcionales

Por supuesto a veces nos interesa que una referencia pueda tener el valor de nil. Para ello, Swift nos ofrece las referencias opcionales:

var optionalUfo : Ufo?

Ahora optionalUfo es una referencia de tipo Ufo? El interrogante final significa "opcional" por lo que dicha referencia puede tomar cualquier objeto Ufo como valor o el valor nil. De hecho, esta referencia se inicializa, por defecto, a nil. Si ahora llamamos a la función UfoName pasándole la referencia optionalUfo (cuyo valor es nil) obtendremos un error (Value of optional type not unwrapped):

swift-nulos2

Este error es debido a que UfoName espera una referencia de tipo Ufo y no una de tipo Ufo? Para "extraer" el valor Ufo que hay dentro de una referencia Ufo? debemos usar el operador postfijo !

UfoName(optionalUfo!)

Por supuesto si la referencia opcional vale nil como es el caso, la función llamada (en este caso UfoName) recibirá un nil incluso aunque ella declaraba que esperaba una referencia de tipo Ufo (que a priori no puede tener el valor nil). En este caso nos encontraremos intentando acceder a una referencia nula:

swift-nulos3

Así pues, es nuestra responsabilidad que al llamar al operador ! nos aseguremos de que la referencia opcional contiene realmente un valor.

Pero Swift nos ofrece todavía más herramientas para ayudarnos a lidiar con posibles referencias nulas. El operador ? es otra de ellas. Dicho operador nos permite acceder al valor de una propiedad opcional solo si dicha referencia contiene un objeto. En caso contrario devolverá nil. Solo podemos usar este operador en referencias opcionales.

Así pues podríamos reescribir la función UfoName como sigue:

 func UfoName (u:Ufo?) -> String? {
    return u?.name
} 

Podemos observar que ahora declaramos que la función acepta una referencia opcional a Ufo y devuelve una referencia opcional a String (es decir puede devolver nil). Ahora ese código es válido y no produce error alguno:

 var optionalUfo : Ufo?
UfoName(optionalUfo 

A pesar de que el valor de optionalUfo es nil, no se genera ningún error porque se usa el operador ? para acceder al valor de la propiedad name en la función UfoName . Por supuesto a pesar de que la función UfoName ahora acepta una referencia opcional a Ufo le puedo pasar una referencia a Ufo:

 var u = Ufo()
u.name = "Zynga"
UfoName(u) 

En este caso UfoName devolvería la cadena "Zynga". Es decir, las referencias no opcionales se convierten automáticamente a referencias opcionales. Lo contrario no es cierto.

Protocolo NilLiteralConvertible

Si una clase implementa dicho protocolo puedes definir como se inicializa una referencia no opcional de dicha clase cuando se le asigna el valor nil. Es decir, eso viene a ser como "redefinir el valor de nil".  Así, si tenemos el siguiente código:

 class Ufo : NilLiteralConvertible {
    required init() {
    }
    var name  = ""
    class func convertFromNilLiteral() -> Self {
        var u =  self()
        u.name = "From nil"
        return u
    }
}

func UfoName (u:Ufo) -> String {
    return u.name
} 

La clase Ufo conforma el protocolo NilLiteralConvertible. Así si definimos una variable Ufo, la inicializamos a nil y llamamos a la función UfoName podremos observar que el valor devuelto es "From nil".

 var u : Ufo = nil
UfoName(u)    // Devuelve "From nil" 

Vemos pues que aunque tenemos una referencia que no es opcional le podemos asignar el valor nil, debido a que la clase conforma el protocolo NilLiteralConvertible. Pero debemos tener presente que a pesar de que le asignamos nil, la referencia termina obteniendo un objeto (el objeto devuelto por el método convertFromNilLiteral).

Por supuesto si usamos una referencia opcional, entonces nil adquiere su significado original:

 var u : Ufo? = nil 

En este caso el valor de u es realmente nil.

Espero que este post os haya ayudado a comprender un poco más cómo funcionan las referencias y los valores nulos en Swift.

Eduard Tomás Eduard es ingeniero informático, atesora muchos años de experiencia como desarrollador y ha sido galardonado como MVP por Microsoft en diez ocasiones. Está especializado en el desarrollo de aplicaciones web y móviles. Mantiene un conocido blog sobre desarrollo en .NET en general y ASP.NET MVC en particular. Colabora con la comunidad impartiendo charlas en formato webcast y en eventos de distintos grupos de usuarios. Puedes seguirlo en Twitter en @eiximenis. Ver todos los posts de Eduard Tomás

Boletín campusMVP.es

Solo cosas útiles. Una vez al mes.

🚀 Únete a miles de desarrolladores

DATE DE ALTA

x No me interesa | x Ya soy suscriptor

La mejor formación online para desarrolladores como tú

Comentarios (1) -

Bueno gracias por él, pero no entendí una cosa ¿por qué se tiene que usar una variable que no contenga nada? ... me dejo entender suponiendo que tengo una app antes de escribir el código debería de tener un algoritmo que ayude a entender el proceso de escribir código es decir por lo menos tener la idea de que objetos se crean, aun no entiendo esa parte de que me sirve crear un objeto en nil en swift?, que ventajas tiene? ... y lo mas importante cual es el motivo de crearlas?

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.