thxou

Ama la sabiduría. Desea el conocimiento.

Literales en Objective-C

| Comments

Los literales son simplemente unos valores que los programadores podemos escribir “tal cual” en el código. En Objective-C (y por lo tanto en C) ya conocemos algunos ejemplos de esto con los valores primitivos:

1
2
float altura = 23.5;
int piezas = 20;

Estos son: un literal float y un literal int respectivamente. Estos literales son comunes en la mayoría de los lenguajes, no obstante Objective-C tiene sus propios literales a parte de los mencionados, concretamente los que están basados en objetos:

1
NSString *tarea = @”Buscar piso;

Claro, a simple vista podemos decir: Vaya chorrada, lo he usado mil veces!, pero detente un momento a pensar en el ahorro que implica en líneas de código el hecho de tenerlos: como escribirías un número “sin números”?, algo aún más terrorífico, plantearse escribir un string de miles de caracteres “sin strings”.

Dicho esto, es fácil darse cuenta de que los literales son una pieza fundamental de cualquier lenguaje por lo que nos ahorran, a parte de por los casos vistos, por la legibilidad en el código, así que vamos a conocer unos cuantos más pero específicos de Objective-C.

La nueva moda en literales

Hasta la salida de la versión 4.0 del compilador LLVM de Apple, Objective-C aún estaba un poco en pañales con respecto a lo que se refiere a literales. Mientras lenguajes basados en C como Perl o Python ya habían incluido literales para colecciones y más, Objective-C se resistía. Ahora eso ya es parte del pasado y vamos a ver cuales son las novedades en literales.

Literales para colecciones

NSArray

Clang, el front end de LLVM, introdujo la sintaxis @[ ] para definir arrays, en el cual solo se deben incluir objetos separados por comas. Ojo!, solo objetos, nada de tipos escalares. Así, lo que antes escribíamos así:

1
2
NSArray *ciudades = [NSArray arrayWithObjects:@"Barcelona", @"Lima", @"Lyon", nil];
</code>

Ahora nos queda así:

1
NSArray *ciudades = @[@”Barcelona, @”Lima, @”Lyon];

Al ser una característica del lenguaje ya no es necesario definir el centinela nil, de hecho si lo pones como valor, tu ordenador explotará, ya que se realiza una validación como para el método [NSArrayarrayWithObjects:count:], en el que se requiere que ningún objeto sea nil. Por lo tanto si quieres pasar nil como valor tendrás que hacerlo con el objeto [NSNull null], que es su equivalente.

NSDictionary

Aquí se introduce la sintaxis @{ }, similar a la de JSON o Javascript, pero con el @ característico de Objective-C. Esta sintaxis crea un diccionario de pares key-value, donde key tiene que ser un objeto que implemente el protocolo NSCopying (los string de toda la vida, vamos!) y value, como en el caso anterior, sólo pueden ser punteros a objetos Objective-C. Algo como:

1
NSDictionary *usuario = [NSDictionary dictionaryWithObjectsAndKeys:@”ThXou, @”nombre, @”Barcelona, @”ubicacion, nil];

Ahora se escribiría:

1
NSDictionary *usuario = @{@”nombre : @”ThXou, @”ubicacion : @”Barcelona};

Como en el caso de los arrays, aquí tampoco es necesaria la centinela nil.

NSSet

Pobre, se olvidaron de él. No se ha introducido nada para esta colección, no obstante con los literales de NSArray se puede aprovechar mucho para NSSet cuando necesitamos pasarlos como argumentos de los métodos inicializadores y métodos de conveniencia.

Literales de NSNumber

Para los que no sabéis cómo definir NSNumber, pues es una clase que nos permite envolver valores escalares (otros literales de tipo int, bool, float, etc) en objetos Objective-C.

Ahora, cualquier valor escalar que empiece por el símbolo ‘@’ devolverá un objeto NSNumber inicializado con ese valor. Esto ya lo veíamos con los strings de C. Cuando escribíamos algo como:

1
NSString *queja = @"El billete de metro está caro";

En realidad estamos convirtiendo un string en C a un objeto NSString con codificación UTF-8. Este literal está desde los inicios, pero para los otros valores escalares usábamos el método numberWithTipo: para inicializar los objetos NSNumber. Ahora haremos:

1
2
3
4
5
6
7
8
NSNumber *bool = @NO; // es equivalente a [NSNumber numberWithBool:NO]
NSNumber *char = @'d'; // es equivalente a [NSNumber numberWithChar:’d’]
NSNumber *unsignedInt = @23U; // es equivalente a [NSNumber numberWithUnsignedInt:23U]
NSNumber *int = @23; // es equivalente a [NSNumber numberWithInt:23]
NSNumber *long = @23L; // es equivalente a [NSNumber numberWithLong:23L]
NSNumber *longlong = @23LL; // es equivalente a [NSNumber numberWithLongLong:23LL]
NSNumber *float = @5.2303F; // es equivalente a [NSNumber numberWithFloat:5.2303F]
NSNumber *double = @2.2808; // es equivalente a [NSNumber numberWithDouble:2.2808]

Me encanta este literal, por lo menos a mi me ayuda haciendo más legible el código, sobre todo cuando tengo que hacer una lectura rápida.

Expresiones “en caja” (Boxed Expressions)

Si la intuición os ha llevado a pensar: ¿Y qué pasa si hago @2+2, el compilador me lo pillará sin enfadarse?. Pues no, ya que hay una nueva sintaxis para esto y es envolver nuestras expresiones entre paréntesis: @(). Esto nos devolvería un objeto NSNumber inicializado con el resultado de la expresión que está entre estos. Y lo que antes hacíamos así:

1
NSNumber *piMedios = [NSNumber numberWithDouble:M_PI / 2];

Ahora lo hacemos así:

1
NSNumber *piMedios = @(M_PI / 2);

Mucho más claro y sencillo. Lo genial es que también funciona pasándole propiedades de algunos objetos Objective-C que devuelven valores escalares. Por ejemplo si tenías que guardar en Core Data el valor de un objeto UISwitch, lo hacíamos así:

1
tarea.completado = [NSNumber numberWithBool:mySwitch.on];

Ahora también lo puedes hacer simplemente así:

1
tarea.completado = @(mySwitch.on);

También funciona para enumeraciones:

1
2
typedef enum { Barcelona, Lima, Lyon } Ciudad;
NSNumber *ciudad = @(Lyon); // nos devolverá 1 y es equivalente a [NSNumber numberWithInt:((int)Lyon)]

En este caso, para poder usar algún valor de la enumeración tenemos que envolverlo también como si se tratara de una expresión para poder usarlo como literal.

Subíndices de objeto (Object Subscripting)

Esta última tanda de literales de la que vamos a hablar suple una necesidad en mi que vengo deseando ver desde que dejé C++ para embarcarme en Objective-C, y tiene que ver con la forma de acceder y obtener datos de colecciones, concretamente de arrays y diccionarios.

Subíndices para Arrays

Para los arrays podemos usar un index para referirnos a la posición de un objeto dentro de ese array, tal como se hacía en C con valores escalares:

1
2
NSArray *ciudades = @[@"Barcelona", @"Lima", @"Lyon"];
NSString *ciudad = ciudades[2]; // ciudad = @"Lyon"

Aquí se nos devuelve el elemento en la posición 2 que es @"Lyon". Esto el compilador lo traduce por su equivalente en Objective-C: [ciudades objectAtIndexedSubscript:2], lo cual es exactamente lo mismo que hacer: [ciudades objectAtIndex:2].

De la misma forma, si tenemos un array mutable, entonces podemos hacer asignación directa de valores con esta misma sintaxis:

1
ciudades[1] = @"Roma"; // el array quedaría: @[@"Barcelona", @"Roma", @"Lyon"]

El valor en la posición 1 es cambiado por el nuevo. Aquí el compilador hace una traducción al método [ciudades setObject:@"Roma" atIndexedSubscript:1]

Subíndices para diccionarios

Para los diccionarios en vez de usar un index usamos keys para obtener los valores:

1
2
NSDictionary *usuario = @{@"nombre" : @"ThXou", @"ubicacion" : @"Barcelona"};
NSString *ubicacion = usuario[@"ubicacion"]; // ubicacion = @"Barcelona"

El compilador hace la traducción a [usuario objectForKeyedSubscript:@"ubicacion"], el cual es a su vez equivalente a [usuario objectForKey:@"ubicacion"].

Como en los arrays, pasa lo mismo para los diccionarios mutables y podemos reemplazar el valor correspondiente a la key que referenciemos:

1
2
usuario[@"nombre"] = @"ThXou soy yo";
// el diccionario quedaría: @{@"nombre" : @"ThXou soy yo", @"ubicacion" : @"Barcelona"}

La traducción correspondiente es [usuario setObject:@"ThXou soy yo" forKeyedSubscript:@"nombre"].

Conclusión

Esta nueva sintaxis como se puede observar, ayuda a que nuestro código sea más legible, a la par que nos ahorra tiempo escribiendo sus métodos equivalentes. Va a ser hora de pasarnos a la nueva moda, nunca es tarde aunque ya lleve unos cuantos meses rulando por internet. Es muy importante recordar pasar únicamente objetos al momento de crear objetos usando literales, como también no pasar nunca un nil como un valor.

Fuentes:

Comments