Introducción a desarrollo en iOS y Mac OSX: El lenguaje Objective-C
Antes de entrar en materia quisiera agradecer al equipo de Geekytheory por la oportunidad de colaborar con ellos. Siendo éste mi primer post me he propuesto hablar de las características del lenguaje de desarrollo de los dispositivos de Apple, Objective-C. Objective-C es un lenguaje de programación orientado a objetos, nacido en la década de los 80, creado como un superconjunto de C. En otras palabras, es posible compilar cualquier programa escrito en C con un compilador de Objective-C, y también se puede incluir libremente código en C dentro de una clase de Objective-C. La interfaz e implementación de una clase se encuentran en bloques de código separados. Al igual que ocurre en C++, las clases de objetos en Objective-C, por lo general se definen mediante un archivo de cabecera y un archivo de implementación: Interfaz de la clase: Clase.h @interface Clase : SuperClase { // Variables de instancia } // Declaración de métodos + (tipo_retorno) metodoClase1; + (tipo_retorno) metodoClase2:(tipo1)param1 otroParametro: (tipo2)param2; - (tipo_retorno) metodoInstancia1; - (tipo_retorno) metodoInstancia2:(tipo1)param1 otroParametro: (tipo2)param2; @end
Implementación de la clase: Clase.m @implementation Clase // Implementación de los metodos de la clase + (tipo_retorno) metodoClase1 { // Implementación } + (tipo_retorno) metodoClase2:(tipo1)param1 otroParametro: (tipo2)param2 { // Implementación } - (tipo_retorno) metodoInstancia1 { // Implementación } - (tipo_retorno) metodoInstancia2:(tipo1)param1 otroParametro: (tipo2)param2 { // Implementación } @end
Al igual que en otros lenguajes orientados a objetos en Objective-C podemos distinguir los métodos estáticos o de clase y los de instancia o singleton. Recordar que un método de clase no puede acceder a las variables de instancia. La declaración de los parámetros de los métodos es algo peculiar. Estos no se indican dentro de un par de paréntesis, sino que se especifican tras dos puntos y poniendo entre paréntesis su tipo. Paso de mensajesEl modelo de programación orientada a objetos de Objective-C se basa en enviar mensajes a instancias de objetos en lugar de llamadas a funciones: [objeto metodoInstancia:param1 otroParametro:param2];
En un lenguaje estilo C++ el nombre del método es en la mayoría de los casos atado a una sección de código en la clase objetivo por el compilador, pero en Objective-C, el mensaje sigue siendo simplemente un nombre, y es resuelto en tiempo de ejecución. El objeto receptor del mensaje tiene la tarea de interpretarlo por si mismo. El objeto al cual es dirigido el mensaje no está inherentemente garantizado a responder a un mensaje, y si no lo hace, simplemente lo ignora y retorna un puntero nulo o lanza una excepción. InstanciaciónUna vez que una clase es escrita en Objective-C, puede ser instanciada. Esto se lleva a cabo alojando memoria para el nuevo objeto y luego inicializándolo: Clase * objeto = [[Calse alloc] init];
La llamada a alloc aloja la memoria suficiente para mantener todas las variables de instancia de un objeto, y la llamada a init puede ser sobrescrita para establecer las variables de instancia con valores específicos al momento de su creación. -(id) init { self = [super init]; if (self) { // Inicialización de las variables de instancia } return self; }
En primer lugar se pasa el mensaje init a la clase madre, identificada con super y a continuación comprobamos si el objeto existe, de ser así, inicializamos las variables de instancia. Tipado dinámicoEn Objective-C un objeto se puede enviar un mensaje que no está especificado en su interfaz. Esto puede permitir una mayor flexibilidad, ya que permite que un objeto "capture" un mensaje y lo envíe a un objeto diferente que puede responder al mensaje de manera apropiada. Este comportamiento se conoce como el reenvío de mensajes o delegación. - (void)setValue:(id)foo; // foo puede ser cualquier clase - (void)setMyValue:(id)foo; //foo puede ser cualquier clase que adopte el protocolo NSCopying
CategoríasDurante el diseño de Objective-C, una de las principales preocupaciones era el mantenimiento de aplicaciones con códigos fuente muy extensos. La experiencia adquirida en el mundo de la programación estructurada había demostrado que una de las principales formas de mejorar el código fue dividirlo en partes más pequeñas. Objective-C amplió el concepto de categorías de implementaciones Smalltalk para ello. Las categorías nos permiten modificar una clase ya existente aunque no dispongamos de su código fuente. La diferencia que hay entre la herencia y la categorización es que la herencia sólo nos permite crear nuevas hojas en la jerarquía de clases, mientras que la categorización nos permite modificar nodos inferiores de la jerarquía de clases. Entero.h# import ; @interface Entero : Object { int entero; } - (int)entero; - (id)entero:(int) param; @end
Entero.m# import "Integer.h" @implementation Integer (int)entero { return integer; } - (id)entero:(int)param { entero = param; return self; } @end
Entero+Aritmetica.h# import "Integer.h" @interface Entero (Aritmetica) - (id)suma:(Entero *)adendo; - (id)resta:(Entero *)sustraendo; @end
Entero+Aritmetica.m# import "Integer+Aritmetica.h" @implementation Entero (Aritmetica) - (id)suma:(Entero *)adendo { return [self entero: [self entero] + [adendo entero]]; } - (id)resta:(Entero *)sustraendo { return [self entero: [self entero] - [sustraendo entero]]; } @end
ProtocolosObjective-C se extendió en NeXT para introducir el concepto de herencia múltiple de especificación, pero no de implementación, a través de la introducción de protocolos. La interfaz de una clase o categoría declara métodos asociados con una clase en partículas. Por el contrario, los protocolos declaran métodos no asociados con ninguna clase, sino que cualquier clase o conjunto de clases pueden implementar. Según esto, los protocolos nos permiten indicar que un conjunto de clases no relacionadas comparten un conjunto de métodos comunes. Los protocolos suelen incluir métodos opcionales, los cuales, si se aplican, pueden cambiar el comportamiento de una clase. Un protocolo informal es una lista de métodos que una clase puede implementar o no. Un protocolo formal es similar a una interfaz en Java o C #. Se trata de una lista de métodos que una clase puede declarar, y de hacerlo ha de implementar. // Protocolos @protocol MiProtocolo - (void)protocoloFormal1; - (void)protocoloFormal2; @optional - (void)protocoloInformal; @end
@interface MiClase : Object - (void)protocoloFormal1 { // Implementación } - (void)protocoloFormal2 { // Implementación } /* Es opcional si adoptamos MiProtocolo - (void)protocoloInformal {} */ @end
#importEn C, la macro #include siempre hace que los contenidos de un archivo sean incluidos en el código en el punto en el que se coloca. Objetive-C tiene la macro equivalente #import, sin embargo se diferencia en que cada archivo se incluye una sola vez por unidad de compilación. PropiedadesObjective-C 2.0 introduce una nueva sintaxis para declarar variables de instancia como propiedades, con atributos opcionales para configurar la generación de métodos de acceso (getter y setter). Las propiedades son, en cierto sentido, variables de instancia públicas, es decir, declarar una variable de instancia como una propiedad permite a clases externas acceder a esa propiedad. @interface Persona : NSObject { @public NSString * nombre; @private int edad; } @property(copy) NSString *nombre; @property(readonly) int edad; -(id)initConEdad:(int)_edad; @end
@implementation Persona @synthesize nombre; @synthesize edad; -(id)initConEdad:(int)_edad { self = [super init]; if (self) { edad = _edad; // asignación directa a la variable de instancia, no se accede al metodo setter de la propiedad } return self; } -(int)edad { return edad; } @end
Gestión de memoriaEn C++ la reserva y liberación de memoria dinámica es responsabilidad del programador y en Java del recolector de basura. En Objective-C nos encontramos en un punto intermedio. Podemos decidir entre gestionar manualmente la reserva y liberación de memoria dinámica, usar el runtime para que nos ayude a llevar la cuenta de la memoria reservada, o bien olvidarnos de la gestión de memoria y dejarla en manos de un recolector de basura. En Objective-C existen tres técnicas de gestión de memoria:
- Manual: el programador se encarga de crear el objeto, y de liberarlo.
- Por cuenta de referencias: el objeto se libera cuando la cuenta de referencias llega a cero.
- Recolector de basura: un recolector de basura se encarga de llevar al cuenta de los objetos referenciados desde el programa que se está ejecutando, y liberar la memoria de los objetos que ya no se pueden alcanzar desde el programa.
Bueno y hasta aquí la lección de teoría. Es imposible resumir todas las características de este lenguaje en una sola publicación, así que las iremos viendo conforme entremos en materia.