Probablemente el principal escollo que se encuentran los programadores que deciden dar sus primeros pasos en el mundo de iOS es el lenguaje que se requiere para desarrollar para los equipos de Apple: Objective-C. Tras unos momentos iniciales de pánico (¿Pero esto no era parecido a C?) llega el momento de realizar mentalmente el cambio de chip y estar dispuesto a dejar de pensar en modo C, C++, C# o Java, y empezar a pensar en Objective-C.
Objective-C apareció en la década de los 80, ante la necesidad de abandonar el código espagueti, y se concibió como un superconjunto de C. Esto quiere decir que la mayor parte del código C será válido en Objective-C sin apenas cambios, aunque la introducción del paradigma de la Programación Orientada a Objetos hace que para sacarle de verdad partido haga falta que nos familiaricemos con su particular sintaxis.
¿Qué ficheros necesitamos?
Al igual que ocurre en C++, las clases de objetos en Objective-C se definen mediante dos archivos:
MiClase.h con las cabeceras e inclusiones necesarias.
@interface MiClase : ClaseMadre { // variables de instancia } // declaración de métodos @end
MiClase.m con la implementación de los métodos de la clase.
@implementation MiClase // implementación de métodos @end
Como veis, la función de cada uno de ellos queda claramente definida en la primera directiva @interface
o @implementation
, y el ámbito queda delimitado por lo comprendido entre ellas y @end
.
Los métodos
Como en muchos de los lenguajes orientados a objetos más comunes, Objective-C nos permite distinguir entre dos tipos de métodos, los estáticos o de clase y los de instancia. La diferencia es que un método de clase no puede acceder a las variables de instancia, siendo indicados para la ejecución de tareas que afecten a toda la clase y no a un solo objeto en particular. La forma de indicar que un método es estático, será anteponiendo un signo +
a su declaración, mientras que los métodos de instancia van precedidos de un -
.
@interface MiClase : ClaseMadre
{ // variables de instancia int variable1; float variable2;
}
// declaración de métodos
+(void)metodoDeClase1;
+(float)metodoDeClase2:(int)parametro1;-(char)metodoDeInstancia1:(NSString *)parametro1 :(int)parametro2;
-(int)metodoDeInstancia2;
@end
El tipo devuelto por los métodos, al igual que en la mayoría de lenguajes orientados a objetos más comunes, se indica antes del nombre de la función y puede ser un tipo elemental o un puntero a un objeto creado por nosotros o contemplado en las librerías propias del lenguaje, como la clase NSString
que es una cadena de caracteres típica de Objective-C.
La declaración de los parámetros de los métodos sí que difiere un poco a la típica de otros lenguajes, ya que no se indican todos dentro de un par de paréntesis, sino que se especifican tras dos puntos y poniendo entre paréntesis su tipo. Por cada nuevo parámetro habrá que introducir otros dos puntos a modo de separador.
Otra diferencia es que a los métodos con más de un parámetro se les suele conocer por el nombre del método seguido de todos los parámetros excepto el primero, es decir que no hablaríamos del método metodoDeInstancia1, sino de metodoDeInstancia1:parametro2
. Así, podríamos definir métodos con nombres como establecerAltura:longitud
o setCoordenadaX:y:z
.
Adiós a las llamadas a funciones, hola a los pasos de mensajes
Y llegamos al punto más conflictivo de nuestro nuevo lenguaje. Hay que olvidarse de las llamadas a funciones, ya que Objective-C no funciona así. El mecanismo que utiliza se llama paso de mensajes y su notación es la siguiente:
[miObjeto metodoDeInstancia1:miParametro1 parametro2:miParametro2]
Aunque en principio se podría decir que la anterior expresión es equivalente a la siguiente en C
miObjeto->metodoDeInstancia1(miParametro1, miParametro2)
lo cierto es que hay algunas diferencias más allá de las sintácticas. En Objective-C se considera que lo que estamos haciendo es pasarle al objeto miObjeto
dos mensajes, metodoDeInstancia1
y parametro2
. El compilador no hace ninguna necesariamente la comprobación de tipo, y será el objeto en tiempo de ejecución el que decide si acepta los mensajes y hace algo con ellos (llamando al método cuyo nombre coincide con el mensaje), o si lo ignora y devuelve un puntero nulo o lanza una excepción.
Una vez superado este pequeño trauma, podremos atrevernos con cosas más complejas como los mensajes anidados, utilizados por ejemplo al instanciar un objeto:
MiClase* miObjeto = [[MiClase alloc] init];
Es importante pensar de dentro hacia afuera para entender bien lo que está sucediendo aquí. En primer lugar, pasamos el mensaje alloc
a MiClase
, lo que reservará el espacio para el objeto y devolverá un puntero al mismo. Y precisamente ese puntero es el receptor del mensaje init
, es decir, que estamos indicando a miObjeto
que se inicialice, siendo algo equivalente al constructor que definiríamos en C++. Una implementación típica de init
suele ser así:
@implementation MiClase
-(id) init
{ self = [super init];if (self) { variable1 = 5000; variable2 = 3.1416; } return self;
} @end
En primer lugar se pasa el mensaje init
a la clase madre, identificada con la palabra super
, y a continuación se establecen los valores de inicio para las variables. Pero si os dais cuenta, antes se comprueba que exista self
, es decir, la instancia del objeto cuyas variables queremos inicializar.
Y ahora, a programar
Tras estos cambios de concepto necesarios para entender Objective-C, ya podremos dedicarnos de lleno al propósito de nuestra aplicación. En futuros posts veremos las particularidades de cocoa e iOS que nos darán la posibilidad de explotar toda la potencia de nuestros equipos Apple, sean éstos de bolsillo o de escritorio.
Más información | Guía Apple de iniciación a Objective-C