You are on page 1of 10

Objective-C para lerdos (que sepan Java o C#)

Primeros pasos:
Lo primero que debes saber es quin cre Objective-C, para saber quin es o son los culpables de tu sufrimiento, y estos fueron Brad Cox y Tom Love a principio de los 80. Bsicamente tomaron C y le aadieron la programacin orientada a objetos de una forma farragosa y fea. Por ello lo primero que deberas saber es como programar en C, si adems sabes C++, Java o C# te ser ms sencillo porque esto no es una gua para ensear los fundamentos de la programacin orientada a objetos, por lo que eso tambin deberas conocerlo antes de leerte esta gua bsica. Es recomendable tambin que mires por si quieres profundizar la documentacin oficial de Apple sobre el lenguaje y la gestin de la memoria, ya que por alguna razn esotrica resulta que en iPhone no hay recolector de basura. Aqu tienes un par de enlaces con informacin. Introduction to The Objective-C Programming Language Memory Management Programming Guide for Cocoa

Creando y manejando clases:


Igual que pasa en C++, tendremos dos ficheros para crear nuestras clases, un .h para definirla y un .m para implementarla. De esta forma tendremos que utilizar #import como seal para que el compilador sepa que clases vamos a utilizar en nuestro cdigo: #import <Foundation/NSObject.h> #import "Hamperdine.h" As que lo primero que hay que hacer es definir nuestra clase en el .h, para futuros usos: @interface NombreDeLaClase : NombreDeLaClasePadre { @private // Declaracin de los atributos privados @protected // Declaracin de los atributos protegidos (opcin por defecto) @public // Declaracin de los atributos pblicos } // Declaracin de propiedades y mtodos @end Cualquiera que haya programado en un lenguaje orientado a objetos tpico como Java o C# ver ciertos elementos familiares, pero as de primeras uno puede ver lo extravagante que resulta este lenguaje con respecto a su sintaxis. Para declarar un atributo es tan simple como: tipo nombre; static tipo nombre; Las propiedades las dejamos para ms tarde de momento, as que vamos a ver como se declara un mtodo de la clase:

// Mtodo esttico de la clase + (tipo) nombre; + (tipo) nombre:(tipo)param1 nombreArgN:(tipo)paramN; // Mtodo de la clase - (tipo) nombre; - (tipo) nombre:(tipo)param1 nombreArgN:(tipo)paramN; Los tipos que podemos usar son los mismos que en C, ms algunos aadidos para Objective-C. Por ejemplo est BOOL, que sirve para representar valores booleanos, siendo YES el equivalente a true y NO el de false. Pero de todos los nuevos tipos id es relativamente importante, ya que representa de forma genrica cualquier tipo de objeto. Realmente es lo mismo que poner NSObject *, que por defecto es la clase padre de cualquier objeto en el lenguaje. Y ya que lo hemos mencionado sera recomendable conocer algunos mtodos principales de esta clase: - (id) init; Es el mtodo para inicializar el objeto, que a efectos prcticos funciona como si fuera el constructor de la clase. - (NSString *) description; Este mtodo se utiliza como representacin alfanumrica de la clase, en C# su equivalente es el mtodo ToString(). - (BOOL) isEqual:(id)obj; Sirve para comprobar si dos objetos son iguales en contenido, ya que si no se sobre-escribe el mtodo su implementacin en NSObject tan solo se limita a comprobar si el objeto actual y el pasado a la funcin tienen la misma direccin en la memoria. Esto solo nos dira que son el mismo objeto, ms que si se tratan de dos objetos distintos pero equivalentes en contenido.

Una cosa curiosa sobre la sobre-escritura de mtodos en las clases hijas, es que no hace falta declarar en la definicin de la clase que vamos a sobre-escribir un mtodo del padre, simplemente se aade en la implementacin y asunto resuelto. No todo iba a ser malo en el lenguaje, no? As que pasemos a mirar la implementacin que ira en el .m: #import "NombreDeLaClase.h" @implementation NombreDeLaClase { // Declaracin de atributos } // Definicin de propiedades y mtodos @end Es parecido a la definicin en el .h, pero para que termine de quedarnos claro pongamos un ejemplo de clase. Primero la definicin en el .h: #import <Foundation/Foundation.h> @interface Entity : NSObject { NSString * name; int x; int y; } + (Entity *) newEntityWithName:(NSString *)nameValue x:(int)xValue y:(int)yValue; - (Entity *) initWithName:(NSString *)nameValue;

- (Entity *) initWithName:(NSString *)nameValue x:(int)xValue y:(int)yValue; // Getters y setters - (NSString *) name; - (void) name:(NSString *)value; - (int) x; - (void) x:(int)value; - (int) y; - (void) y:(int)value; @end Y ahora la implementacin de la clase en el .m, en el que aparecern algunas cosas que luego se explicarn con ms detalle, por ahora tan solo es necesario quedarse con el patrn: #import "Entity.h" @implementation Entity + (Entity *) newEntityWithName:(NSString *)nameValue x:(int)xValue y:(int)yValue { return [[Entity alloc] initWithName:nameValue x:xValue y:yValue]; } - (id) init { self = [super init]; if(self != nil) { name = @""; x = 0; y = 0; } return self; } - (Entity *) initWithName:(NSString *)nameValue { self = [super init]; if(self != nil) { name = [nameValue retain]; x = 0; y = 0; } return self; } - (Entity *) initWithName:(NSString *)nameValue x:(int)xValue y:(int)yValue { self = [super init]; if(self != nil) { name = [nameValue retain]; x = xValue; y = yValue; } return self; }

- (void) dealloc { [name release]; } - (NSString *) name { return name; } - (void) name:(NSString *)value { if(name != value) { [name release]; name = [value retain]; } } - (int) x { return x; } - (void) x:(int)value { x = value; } - (int) y { return y; } - (void) y:(int)value { y = value; } @end Vemos varias cosas familiares como super para invocar mtodos de la clase padre y self, que no hace falta ser muy intuitivo para imaginarse que hace la misma funcin que this (en C++, Java o C#), que es el puntero o referencia a la propia clase. Luego estn las llamadas retain y release que ya veremos para qu sirven en el captulo dedicado a la gestin de la memoria. Pero para entenderlo ya del todo veamos una aplicacin de ejemplo sencillita: #import "Entity.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Entity * guy01 = [[Entity alloc] newEntityWithName:@"Peter"]; Entity * guy02 = [Entity newEntityWithName:@"John" x:10 y:20]; NSLog(@"%@ = (%d, %d)", [guy01 name], [guy01 x], [guy01 y]); NSLog(@"%@ = (%d, %d)", [guy02 name], [guy02 x], [guy02 y]); [guy01 release]; [guy02 release];

[pool release]; return 0; } Con este ejemplo vemos como crear instancias de objetos, pidiendo espacio en memoria para ellos con alloc e inicializndolos con init o sus variantes dependiendo del objeto. Tambin est en el ejemplo una llamada a NSLog que funciona parecido a printf pero para poder trabajar con las cadenas nativas de Objective-C que son las NSString y que para indicarlas como valores literales en el cdigo tenemos que empezar la cadena poniendo una @ delante. Para indicar en la cadena de formato que va un objeto hay que usar %@ que har que NSLog invoque el mtodo description del objeto para formar la cadena que mostrar finalmente en pantalla. Otras curiosidades es que para indicar el valor nulo (null en Java y C#) se emplea nil. Tambin existe una funcin assert que se llama NSAssert para parar la ejecucin si no se cumple alguna condicin necesaria mientras estamos depurando la aplicacin.

La ruin gestin de la memoria:


Objective-C 2.0 introdujo un recolector de basura, porque seguramente la mayora de la gente estaba harta del sistema tradicional para gestionar los objetos de este lenguaje. Pero para desgracia del personal, resulta que en iPhone no est soportada esta novedad de la versin 2.0, con lo que hay que hacerlo al modo antiguo. La idea es parecida a la idea de los punteros inteligentes, donde cada instancia tiene un contador de referencias y cuando este llega a cero, el objeto ser liberado de la memoria. La idea es buena en el fondo, para evitar ciertos problemas que ocurren en C++, pero no es que la vida se vuelva ms sencilla precisamente. Cada vez que creamos un nuevo objeto con alloc, new, copy o mutableCopy, el contador de referencias se pone a 1, a partir de entonces cada vez que lo referenciemos desde otro puntero, tendremos que llamar a retain para aumentar el contador. De la misma forma, cada vez que vayamos a dejar de darle uso, deberemos de invocar a release para disminuir el contador. Tambin debemos tener en cuenta que por lo general todas las colecciones de datos del API, como NSArray o NSMutableArray, al aadir un objeto en ellas, se les aplica el mtodo retain de forma automtica, es decir que los objetos de la API oficial de Apple a los que les pasemos objetos creados por nosotros, para almacenarlos en su interior los retendrn aumentando el contador y los liberarn cuando este objeto sea destruido (llamando a release). Por ello en los mtodos de nuestras clases, por ejemplo en los setters tendremos que actualizar la cuenta para evitar en algn momento dado intentar acceder a algo que ya no existe. Por ejemplo: - (void) name:(NSString *)value { if(name != value) { [name release]; name = [value retain]; } } Aqu miramos primero que no estamos asignando la misma instancia que ya tenamos asignada. Si no es la misma, liberamos la anterior y retenemos la nueva. Por suerte retain devuelve el propio objeto, por lo que podremos asignar y retener en la misma lnea sin problemas. Pero no todo es amor en la villa del seor, porque si por ejemplo, como suele ocurrir con el mtodo description, devolvemos una cadena y le aplicamos el release antes de devolverla se habr borrado sin que la hayamos podido retener desde el mtodo invocador. Pero si no hacemos nada, esa variable no se va a eliminar y muchas veces, como suele

ocurrir con las cadenas, ni siquiera las vayamos a asignar a un puntero nuestro, sino que lo que nos devuelva el mtodo se lo pasamos a otro y ya est. Al final tendramos un memory leak serio. La solucin a esto consiste en hacer lo siguiente, aplicndolo a Entity por ejemplo: - (NSString *) description { NSString * result = [[NSString alloc] initWithFormat:@"%@ = (%d, %d)", name, x, y]; [result autorelease]; return result; } El mtodo autorelease es utilizado para aadir al objeto invocador al ltimo NSAutoreleasePool creado en el momento de la llamada. Por decirlo de algn modo esta clase es un recolector de referencias de objetos perdidos, que dispone de un mtodo llamado drain (que es llamado tambin desde el mtodo release), que lo que hace es borrar todos los objetos que ha recolectado hasta ese momento. No es tan elegante como un recolector de basura, pero es la nica opcin sencilla. Estos recolectores pueden ser anidados sin que haya problemas, por lo que son una herramienta til para asegurarnos de eliminar basura despus de algn algoritmo que sepamos que genera muchos objetos que se quedan en el limbo. Y qu pasa si queremos retener un objeto que ha sido marcado como autorelease? Pues que simplemente con llamar a retain se quita del recolector y pasamos a hacernos cargo de su vida nosotros. As que todos los mtodos que devuelven un objeto nuevo (que no sean alloc, new, copy o mutableCopy), como description, aaden con autorelease el objeto devuelto al recolector activo en ese momento. Adems de todo lo anterior, para que funcione este sistema tendremos que sobrecargar el mtodo dealloc, que a efectos prcticos es como el destructor de la clase, al llegar el contador de referencias a cero, se llama a dealloc y luego se borra el objeto. En el ejemplo de Entity veamos que tan solo tenamos un objeto, name, por lo que ese era el nico que tenamos que liberar.

Declarando y manejando propiedades:


En el ejemplo anterior hemos creado getters y setters para poder acceder a los atributos de la clase, pero en la versin 2.0 del lenguaje se incorpor una forma menos dura y ms natural, que consiste en una idea similar a las propiedades de C#. Primero hay que definir la propiedad en la interfaz de la clase con: @property tipo nombre; @property (atributos) tipo nombre; Como se puede ver, los atributos, son opcionales y ya veremos cules son los que actan por defecto: Mtodos para acceder: o getter=nombre Por defecto, si no se indica nada, el getter tendr el mismo nombre que el identificador de la propiedad. o setter=nombre Por defecto, si no se indica nada, el setter tendr como nombre al identificador de la propiedad precedido de la palabra set. Por ejemplo, si nuestra propiedad se llama name, el setter por defecto sera setName. Escritura o solo lectura: o readwrite Es una de las opciones por defecto en la definicin de las propiedades, por lo que no hace falta ponerlo, y lo que permite es que se pueda leer y escribir su contenido. o readonly Esta opcin indica que la propiedad es solo de lectura. Tipo de asignacin:

assign Se realiza la asignacin sin invocar a retain. Este tipo se suele utilizar para propiedades de tipos que no son objetos como int. Por defecto una propiedad utiliza esta opcin, por lo que es algo que se suele indicar al definir las propiedades, ya que lo normal es trabajar con objetos con bastante frecuencia. o retain Se realiza la asignacin invocando a retain. Este tipo solo se puede utilizar para propiedades de tipos que son objetos de Objective-C. o copy Se le asigna a la propiedad una copia del objeto pasado al setter. Atomicidad: o atomic Es la opcin por defecto y lo que hace es bloquear la asignacin para que ningn hilo que est ejecutndose al mismo tiempo, pero no haya sido el que ha invocado al setter, no pueda modificar la propiedad mientras lo est haciendo otro hilo. o nonatomic En esta opcin no hay bloqueo, por lo que cualquier hilo puede modificar el valor sin tener que esperar a que termine el otro. Esto obviamente puede ser problemtico, pero en aplicaciones que solo tienen un hilo se utiliza esta opcin para ahorrar operaciones intiles que no serviran de nada.

Definidas las propiedades en nuestra clase viene la implementacin y para ello se utiliza: @synthesize nombre; @synthesize nombre = nombreAtributo; @dynamic nombre; Con @synthesize lo que le estamos indicando al compilador es que si el programador no ha creado los mtodos a mano, que los cree automticamente. Y con @dynamic se le indica que el programador es el que se va a encargar de crear los mtodos para la propiedad, por lo que el compilador no har nada y si no aparecen dar error al compilar. Obviamente si se crean a mano han de cumplir las opciones indicadas en la definicin de la propiedad, para evitar problemas en la ejecucin. Existe la opcin de sobre-escribir propiedades definidas en clases padres, protocolos (que son el equivalente a las interfaces de Java o C#) o de las categoras, pero tan solo se puede cambiar opcin sobre si es de lectura/escritura o solo de lectura no puede ser alterada en la sobre-escritura, el resto tendrn que ser iguales a la definicin original. Luego tenemos algunas peculiaridades sobre las propiedades. Por ejemplo al indicar la opcin copy tendremos problemas si el tipo de la propiedad es un objeto mutable, porque el mtodo copy devuelve objetos que no se pueden modificar despus de inicializados. Por ello, el programador tendr que crear a mano el setter y usar mutableCopy como mtodo para copiar el objeto pasado al mtodo. Otra peculiaridad rara tiene que ver con dealloc, ya que por extraas circunstancias las propiedades de objetos que son sintetizadas no te permiten invocar a release desde dealloc, que es la forma normal de trabajar cuando no se usa propiedades. Lo que tienes que hacer es asignarle el valor nil a la propiedad de alguna de las dos siguientes formas: // Forma A [self setNombre:nil]; // Forma B self.nombre = nil;

Y todo esto es lo que hay que hacer para poder utilizar el operador punto, para acceder a los atributos de la clase, en vez de utilizar un mtodo get o set, y lograr que programar en este lenguaje parezca ms natural.

Los protocolos:
Para los programadores de Java y C# las interfaces son un concepto bien conocido y til, ya que en esos lenguajes para evitar que la gente se fuera de madre se elimin la opcin de la herencia mltiple. Objective-C tampoco se puede tener ms de un padre, por lo que existe lo mismo, pero se le llama protocolo porque as queda ms cool o yo qu demonios s. El caso es que se declaran as: @protocol Nombre <NombreProtocoloPadre1, ... , NombreProtocoloPadreN> // Declaracin de mtodos cuya implementacin es obligatoria @optional // Declaracin de mtodos cuya implementacin es opcional @required // Declaracin de mtodos cuya implementacin es obligatoria @end Lo primero que uno puede ver es que los protocolos pueden heredar de uno o varios protocolos, que tambin pasa en otros lenguajes utilizados hoy en da. Lo segundo y sorprendente es que existen mtodos que podemos indicar como opcionales su implementacin. Y lo tercero es que la seccin @required es completamente intil, ya que en la seccin por defecto acta de la misma forma. Esta es la forma formal para especificar un protocolo, existe una informal que consiste en hacer una categora (un concepto que se ver de forma ms extensa en el siguiente apartado de la gua) sobre NSObject, con lo que todo el mundo tendra los mtodos de este protocolo informal: @interface NSObject (Nombre) // Declaracin de mtodos @end El caso es que siguiendo con los protocolos formales, la forma de indicar que una clase va a implementar uno en particular es la siguiente: @interface NombreClase <NombreProtocolo1, ... , NombreProtocoloN > { // Declaracin de atributos } // Declaracin de mtodos y propiedades @end Pero para anticipar a los problemas existe un par de mtodos interesantes para comprobar si podemos trabajar tranquilamente con una clase, el primero es conformsToProtocol, que comprueba si el objeto que lo invoca tiene implementados todos los mtodos obligatorios de un protocolo. El segundo es respondsToSelector, que comprueba si existe un mtodo en el objeto que lo invoca. Que ya dicho sea de paso, para Objective-C los selectores son algo as parecido a los punteros a funciones, que podemos pasrselo a una funcin para que esta lo use invocndolo, pero como es un tema complicado lo mejor es

mirar la documentacin oficial para comprender el tema mejor. De momento nos quedaremos con un ejemplo de cmo se utilizan los dos mtodos que acabamos de comentar: BOOL result = [nombreObjeto conformsToProtocol:@protocol(NombreProtocolo)]; BOOL result = [nombreObjeto respondsToSelector:@selector(nombreMetodo)]; Al indicar el tipo de una variable podemos asignarle una instancia de esa clase o la de alguna clase hija en su jerarqua. Con los protocolos tambin podemos hacer lo mismo, para limitar la clase de datos que se pueden instanciar en una variable: NombreClase <protocolos, ...> * nombreVariable; id <protocolos, ...> * nombreVariable; De la primera forma tendr que cumplir los protocolos indicados y ser parte de la jerarqua de la clase indicada. De la segunda forma puede ser cualquier clase, pero tendr que cumplir los protocolos indicados.

Las categoras:
Puede ocurrir que uno desee extender la funcionalidad de una clase cualquiera y las categoras es la forma de realizar esto (en otros lenguajes dinmicos existen cosas parecidas, al igual que en C#). La sintaxis para definir e implementar una categora es sencilla, detrs del nombre de la clase se pone entre parntesis el nombre de la categora y asunto resuelto: #import "Victima.h" @interface Victima (Nombre) // Definicin de los miembros @end En el fichero de implementacin .m ira un cdigo parecido a este: #import "Victima+Nombre.h" @implementation Victima (Nombre) // Implementacin de los miembros @end Razones para extender clases existentes hay muchas y que cada cual encuentre la que ms rabia le d. Se pueden sobre-escribir mtodos de las clases superiores en la jerarqua, pero obviamente no se puede hacer lo mismo con las de la clase u otras categoras anexionadas a esta clase. Adems hay que tener cuidado de qu mtodos aadimos y a quin, porque podra provocar que implosione nuestro programa.

Enumeracin rpida:
En las colecciones de datos existe lo que se llama un enumerador que sirve para recorrer la estructura, obteniendo uno a uno los datos que contienen. Por suerte alguien se dio cuenta de que era un coazo no tener una forma ms sencilla de especificar bucles para recorrer las estructuras, as que se cre en Java y C# una alteracin para la instruccin for. Por suerte en Apple a alguien tambin se le ilumin la cabeza y aadieron la misma idea:

for(tipo variable in coleccin) { ... } Esta instruccin se le puede aplicar a cualquier coleccin que implemente el protocolo NSFastEnumeration. Para ver un ejemplo de cmo ira esto: NSArray * numbers = [NSArray arrayWithObjects: @"ichi", @"ni", @"san", @"shi", nil]; for(NSString * number in numbers) { NSLog(@"Nmero: %@", number); } Tambin los enumeradores tienen implementado el protocolo para la enumeracin rpida, por lo que podemos utilizar alguno de ellos en alguna ocasin, como por ejemplo para recorrer una estructura al revs.

Manejando excepciones:
Todo el que ha programado en un lenguaje orientado a objetos conoce lo que son las excepciones y sino aprended POO primero por dios. As que pasaremos a la parte en la que vemos lo parecido que es a otros lenguajes como Java o C#. La clase base para excepciones que trae el SDK de Apple se llama NSException y en este ejemplo descubriremos como lanzar una: NSException *exception = [NSException exceptionWithName:@"EscozorInfernal" reason:@"Me escuece horrores programar en esta m***da" userInfo:nil]; @throw exception; Ahora sabemos lanzarlas, pero hay que saber cmo capturarlas y la forma es idntica a otros lenguajes pero con el estilo particular de Objective-C: @try { // Cdigo sospechoso de fallar como una escopeta de feria } @catch (NSException * exception) { // Cdigo para manejar la excepcin capturada } @finally { // Cdigo que se ejecutar siempre, pase lo que pase } Como se puede observar el esquema es idntico al que se usa en Java o C#, as que nada nuevo bajo el cielo. Si queremos capturar cualquier tipo de posible excepcin, incluso las que no heredan de NSException, podemos poner (id exception) en el bloque @catch, para capturar cualquier cosa.

Ms all se encuentra un mundo todava peor:


Hay ms cosas que no he comentado en esta gua que son interesantes, como la interaccin de nuestras clases controladoras con la interfaz creada desde el Interface Builder (los marcadores IBOutlet e IBAction), el tema de los selectores, las colecciones de datos del SDK oficial, etctera. Pero existen tutoriales y videotutoriales bastante buenos por internet para aprender a desarrollar aplicaciones con Xcode, Cocoa y CocoaTouch. Tan solo espero que esta gua os pueda servir para conocer la base mnima con la que trabajar en este maravilloso lenguaje. Un saludo, Gorka Surez.

You might also like