Categorías en Objective-C

enero 30, 2010

A menudo tenemos que ampliar el comportamiento de una clase, y muchas de esas veces esa clase es parte de una librería que no esta desarrollada por nosotros. Habitualmente la forma de ampliar este comportamiento es usando la herencia, nuestra clase heredaría de la que queremos ampliar su funcionalidad y implementaría dichos métodos. Esto es lo que la teoría de orientación a objetos nos cuenta, pero existen otros métodos menos acordes a dicha teoría pero que aportan otros detalles que algunas veces pueden ser utiles.

Estas técnicas pasan por definir los nuevos métodos dentro de la propia clase, como si la estuvieramos ampliando. Así por ejemplo podríamos ejecutar un código como

NSString *cadena = [NSString initWithString:@"Hola que tal"];
NSLog(@"Numero de vocales: %@", [cadena cuentaVocales]);

Cuando sabemos que inicialmente la clase NSString no acepta el mensaje “cuentaVocales”.

Como suele ser habitual, estas cosas tienen su lado bueno y su lado malo.

La principal ventaja es que los que usen nuestra librería, que amplia la clase NSString, no tienen que construir ninguna clase nueva para utilizar estos nuevos métodos, lo que facilita la forma que tienen los usuarios para adaptarse a nuestro código, y si nos ponemos exigentes desde el punto de vista de rendimiento nos ahorramos tener que llamar a la mayoría de los métodos de la clase padre siempre a traves de la clase hija.

Otro de los beneficios es que si tenemos ya métodos que admiten parámetros del tipo de la clase padre, usando este forma de extension de clases podremos acceder a los métodos modificados, sin necesidad de cambiar los tipos de los parámetros.

Por ejemplo, si tenemos un metodo como:

void imprimir_estadisticas: (NSString *)cadena
{
  NSLog(@"Numero de letras: %d", [cadena length]);</p>
  // Podriamos añadir lo siguiente, sin necesidad de cambiar el tipo del parametro.</p>
  NSLog(@"Numero de vocales: %d", [cadena contarVocales]);</p>
}

Este concepto no es exclusivo de Objetive-C, de hecho en muchos otros lenguajes se pueden ampliar tambien las clases. Por ejemplo en C# podemos usar la definición parcial de clases para añadir nuevos métodos, mientras que en Javascript por ejemplo, podemos añadir nuevos metodos modificando la propiedad “prototype”.

Ahora que sabemos en que consisten las categorías, vamos a ver como podemos implementarlas usando Objetive C

Por ejemplo, vamos a ver como añadir una categoría que por un lado anadirá un nuevo método a la clase NSString para contar el numero de “aes” minusculas de una cadena; y por otro, sobreescribirá el metodo capitalizedString que ya posee la clase NSString.

Empezemos con el prototipo, la forma de declarar una categoría es:

@interface NSString (PruebaCategorias)
  - (int) countLowerA;
  - (NSString*) capitalizedString;
@end

Donde normalmente va el nombre de nuestra clase, en este caso se coloca la clase que queremos extender, y entre parentesis el nombre de nuestra categoría. Lo que viene a continuación son la definición de los métodos que forman la categoría.

La parte de la implementación es similar a cualquier implementación de una clase, salvo por el nombre de la categoria. Nuestro ejemplo quedaría:

@implementation NSString (PruebaCategorias)

- (int) countLowerA
{
	int cont = 0;
	for ( int i = 0; i < [self length]; i++)
		if ( [self characterAtIndex:i] == 'a' ) cont++;

	return cont;
}

- (NSString *) capitalizedString
{
	return @"HOLA!";
}
@end

Se recomienda usar una nomenclatura fija para nombrar los ficheros que poseen esta definición, esta nomenclatura sería para nuestro ejemplo “NSString+Pruebacategoria.m/h“, es decir, usar el nombre de la clase que se amplía, el caracter ‘+’ y el nombre de nuestra categoría.

Finalmente, si usamos el siguiente código para probar la categoría:

NSString *str = @"Hola mando";
NSLog(@"Capitalized: %@, As: %d\n", [str capitalizedString], [str countLowerA]);

Se nos mostraria por pantalla

"Capitalized: HOLA!, As: 2" // Esta claro que capitalizedString no funciona como esperábamos.

Antes dijimos que esta forma de extender el comportamiento de las clases tenia sus lados buenos y sus lados malos pero al principio sólo contamos los buenos. Una de las grandes desventajas de ampliar el comportamiento de esta manera es que las ampliaciones pueden ocultar métodos ya existentes, como vemos en el ejemplo. Incluso, esto puede pasar de desventaja a peligro de seguridad. En nuestro caso, hemos “ocultado” el método capitalizedString, de tal manera que si una tercera persona usa nuestra liberia, cuando llame a capitalizedString, se encontrará un comportamiento no esperado, poniendose paranoico, incluso capitalizedString podria borrar todos sus Documentos del Disco Duro.

Además dada la interaccion entre las diferentes clases del framework, ocultar un método podria hacer que muchas cosas dejaran de funcionar, ya que cuando esas otras clases llamen al método que hemos ocultado se encontrarán con que no funciona de forma adecuada. Por ejemplo, supongamos que sobreescribimos el método windowWillClose de la clase NSWindow, seguramente obtengamos cosas no deseadas en nuestra aplicación Cocoa despues de hacer esto.

Como vemos, lo que al principio nos parecia una gran idea, ahora vemos que tiene algunos factores que nos indican a estar muy alerta. Particularmente pienso que no es la forma mas “limpia” de hacer las cosas, si uno quiere ampliar el comportamiento de una clase lo mas idoneo es extender dicha clase. Sin embargo en casos muy puntuales, como los citados al principio de la entrada, podría ver justificado su uso.


Hardware con Arduino

enero 9, 2010

Una de las cosas que dejaron por aquí los reyes magos ha sido una placa Arduino. Hace tiempo estuve mas o menos metido en el tema de la electrónica, y lo cierto es que es un mundo que me parece apasionante, aunque uno de los grandes problemas era lo dificultoso que era el punto de entrada. Construirse una placa sencilla con resistencias o leds no era del todo complicado, pero cuando querias ir algo mas allá, y entrabas en los microcontroladores, la cosa se complicaba. En mis tiempos de estudiante, Microchip tenía una placa de desarrollo basado en PICs que estaba bastante bien, hasta llegamos a hacer algo curioso en la universidad, pero el precio era un obstaculo importante. Incluso, no era muy complicado llegar a cargartela conectando de forma incorrecta las cosas.

Arduino viene a eliminar algunas de estas barreras que hacian los primeros pasos algo complicados.

El primer detalle que salta a la vista es que la placa Arduino es Hardware Abierto, esto es, el diseño de la placa esta totalemte disponible, y cualquiera se podría fabricar una, por otro lado, las herramientas de desarrollo también son libres, por lo que podemos desarrollar sin ningun coste. Como ya sabemos el hecho del software libre, no solo implica a los costes, sino que también suele significar que hay una gran comunidad detrás apoyando al proyecto, y en el caso de arduino, la hay.

Además podemos hablar del precio, una de las grandes ventajas de arduino precisamente es esta, el precio de la placa de entrada, la Duemilianove esta por debajo de los 30€, lo cual lo hace perfecto para empezar. Incluso, los modelos algo mas avanzados no llegan a los 100€

El segundo punto fuerte de Arduino es la facilidad de desarrollo, desde su sitio web podemos descargarnos un entorno de desarrollo multiplataforma ( ya que esta hecho en Java ). Mediante un lenguaje similar a C, podemos escribir con muy poco esfuerzo un programa, subirlo a nuestra placa pinchando en un botón y ejecutarlo.

Por ejemplo, el código de ejemplo que hace que un led parpadee seria algo como:

int ledPin =  13;    // LED connected to digital pin 13
// The setup() method runs once, when the sketch starts
void setup()   {
// initialize the digital pin as an output:
  pinMode(ledPin, OUTPUT);
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()
{
  digitalWrite(ledPin, HIGH);   // set the LED on
  delay(1000);                  // wait for a second
  digitalWrite(ledPin, LOW);    // set the LED off
  delay(1000);                  // wait for a second
}

En mi caso tardé 10 minutos en tener el programa cargado y ejecutandose en la placa. Todo un logro y que dice mucho de la facilidad con la que podemos hacer cosas.

A nivel técnico, el corazón de la placa es un chip Atmega de la empresa Atmel, este chip tiene una arquitectura AVR. Esto significa que podemos usar el toolchain para gcc sobre arquitecturas AVR, llamado avr-gcc. Aparte de las herramientas de compilación, también existe un port de la libreria estandar de c para esta arquitectura, por lo que además de poder programar usando la variación de C que se nos ofrece en el IDE, también podremos programar directamente en C, compilando desde la linea de comandos