Primeros Pasos
Como buen lenguaje de programación orientado a objetos Objective C permite el uso de clases en el código. A continuación veremos varios ejemplos de uso de las Clases.
Empecemos por dar de alta una clase en Xcode en un proyecto cualquiera. Para ello deberemos pulsar Comando + N para un crear un nuevo fichero, a continuación aparecerá el siguiente diálogo:
En la parte izquierda podemos ver los distintos tipos de ficheros que podemos crear. Seleccionaremos Cocoa o Cocoa touch dependiendo de si estamos haciendo una aplicación para Mac OSX o para iOS y pulsamos en Next, a continuación nos preguntará por el nombre de la clase y su herencia:
Pondremos el nombre de la clase en el campo Class e indicaremos en el selector, o escribiremos el nombre directamente, de la Clase Padre de la nueva clase que vamos a crear.
NSObject es la Clase principal del Objective C, aquellos que vengan de Java verás que es muy similar en concepto y métodos a los que nos encontramos en la clase Object.
Por lo tanto para empezar rellenaremos el nombre con “MiClase” y dejaremos en el selector “NSObject” por simplificar la primera clase.
Una vez rellenado este paso del asistente pulsamos en Next y nos saldrá el dialogo para indicar donde queremos guardar la nueva clase:
Seleccionaremos la carpeta donde se guardará la clase en la parte superior y en la inferior indicaremos el grupo, que es la manera que tiene de denominar Xcode las carpetas virtuales dentro del proyecto. Dentro de la carpeta del proyecto tenemos una carpeta con ese mismo nombre. y es donde se situará los ficheros de la clase.
El target o el destino de la clase, como puede verse en la captura 01_clases es el nombre del proyecto y el target de compilación y ejecución del mismo. Lo dejamos seleccionado y pulsamos en el botón Create.
Esto nos creará los ficheros en el proyecto y nos abrirá automáticamente el fichero de implementación “.m” en el editor, tal como puede verse en la captura:
También podemos ver cómo en la parte izquierda tenemos dos ficheros correspondientes a la nueva clase. El fichero .h o de cabecera (sí como en C) y el fichero .m de implementación.
Como derivado de C disponemos de los dos ficheros, uno para las definiciones y otro para la implementación.
Veamos el contenido del fichero .h
//
// MiClase.h
// 01_clases
//
// Created by pepesan on 10/03/14.
// Copyright (c) 2014 pepesan. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MiClase : NSObject
@end
En este caso, a parte de los típicos comentarios explicatorios de la clase, tenemos un import
#import <Foundation/Foundation.h>
Que permite importar, como el C, un fichero de cabecera de una biblioteca de sistema, de ahí los <>, en este caso la biblioteca Fundation. Esta biblioteca es la que dispone de la mayor parte de las clases de objetos de Objective C por lo que deberemos importarla siempre.
En el fichero de cabecera, el .h, la clase se enmarca entre el @interface y el @end
Después del @interface se coloca el nombre de la clase, en este caso Miclase, seguido de un : y el nombre de la clase padre en este caso NSObject .
Por otra parte en el fichero .m, el de implementación nos encontramos con lo siguiente:
#import “MiClase.h”
@implementation MiClase
@end
De la misma manera deberemos importar el fichero de cabecera de la clase:
#import “MiClase.h”
Y la implementación estará enmarcada entre las anotaciones @implementation , seguido del nombre de la clase Miclase y terminando con el @end .
Objetos
A continuación veremos con instanciar objetos en Objetive C.
Instanciación de Objetos
Para instanciar un objeto de una clase es necesario importar el fichero de cabecera desde el fichero en el cual queramos usar la clase.
En este caso podríamos en el fichero main.m, incluir por encima de la función main lo siguiente:
De este modo indicamos al fichero que importe dicha clase, a continuación dentro de la función main podremos realizar la declaración de un objeto:
Como puede verse primero se escribe la clase a la que va a pertenecer dicho objeto y luego el nombre del objeto, con un * delante, aquellos que conozcan C o C++ verán claramente que se trata de un punto al objeto, en el momento de la declaración dicho puntero apuntará a nil que es como se llama en Objective C a null. A continuación podremos inicializar el objeto:
objeto=[[MiClase alloc] init];
esto puede parecer un poco extraño al principio pero leyéndolo con detenimiento tiene sentido la sentencia.
Lo primero de todo es que al ser una inicialización es una asignación por lo que colocamos en la parte izquierda el nombre del objeto que deseamos inicializar. En la parte derecha de la sentencia vemos dos conjuntos de corchetes, empezamos por el más interno:
De esta manera indicamos que queremos reservar memoria para el objeto algo muy parecido a un new en Java o un alloc en C. En todos los casos se indica que queremos reservar memoria y el nombre de la clase.
La definición de la función alloc es la siguiente:
– (id) alloc;
El tipo de dato
id es una representación de un NSObject *, pero no es necesario poner el * en este caso, ya que se sobreentiende. Sería lo más parecido a que una función Java devolviera
this, refiriéndose a la clase
Object .
Se ejecuta el método de la clase alloc esta es la primer sorpresa en Objetive C. Para ejecutar métodos, que se denomina paso de mensajes en Objective C, se coloca el nombre de la clase un espacio y a continuación el nombre del método que se desea ejecutar, todo ello rodeado de los corchetes.
Para ejecutar métodos estáticos, es exactamente igual, salvo que en vez de poner el nombre de la clase se indica el nombre de la clase.
Una vez ya reservada la memoria del objeto es necesario inicializarlo mediante su constructor, que en Objective C, se llama
init y se vuelven a colocar los corchetes antes del “objeto” y después del
init .
La declaración de la función init es la siguiente:
– (id) init;
Como en el caso anterior también devuelve
id .
Nota de codificación: Prueba a escribirlo de la siguiente manera:
- primero hay que declarar el objeto
- escribe el nombre de la clase, verás que Xcode te ayuda a rellenarlo, según vas pulsando se va acercando a la clase que buscas, cuando la tengas seleccionado, pulsa enter
- no te olvides de poner el * entre la clase y el nombre del objeto. Si no lo haces Xcode te saltará un aviso de que no lo has puesto y cómo arreglarlo, poniéndolo XD
- escribe el nombre de la variable del objeto
- luego escribe el =
- Luego pon un abre corchete [
- Escribe el nombre de la clase, verás que Xcode te ayudará a rellenarla y te aparecerá el corchete de cierre en un tono de gris, como si no estuviera escrito todavía pero que con un gesto podríamos ponerlo, esto se ve más adelante.
- pon un espacio y una a, verás que Xcode también completa los nombres de métodos
- selecciona alloc pulsando enter
- pulsa el botón derecho del teclado o el tabulador, verás como se escribe finalmente el cierra corchete, poniéndolo el color negro definitivo
- escribe in, verás que sale el método init
- pulsa enter y se escribirá el método completo
- escribe un cierra corchete y verás como justo delante del nombre de la clase
- pon el ; al final
Como verás el Xcode nos ayuda bastante a buscar los nombres del las clases objetos y métodos. Si sigues esta guía de pasos tendrás menos problemas al codificar y cometerás menos fallos de escritura.
El constructor init es el constructor principal de todas las clases Objective C. La norma dice que todos ellos deben empezar el nombre con la palabra init en minúsculas.
Como vemos los nombres de clases siguen la nomenclatura CamelCase, con alguna excepción. Todas las palabras que compongan el nombre deberán llevar la primera letra en mayúscula y el resto en minúscula, de ahí el nombre MiClase. Muy similar a como se hace en Java.
La excepción a esta regla es que las clases Objective C suelen disponer de un prefijo que indica la compañía que la ha realizado. En la mayor parte de las clases de la biblioteca Fundation empiezan por NS, referiéndose a la empresa Next Step creadora de esta biblioteca.
Propiedades de Objetos
Inclusión de Atributos (propiedades)
Para incluir un atributo hay muchas maneras de realizarlo pero la más sencilla es su inclusión en el fichero de cabecera .h, dentro de la definición de la clase, veamos un ejemplo:
como puede verse se antepone a la definición del atributo la anotación @property y después se declara normalmente la variable, poniendo primero el tipo y luego el nombre de la variable. Lo mismo se haría con los objetos de clase.
Características de una propiedad (property attributes)
- getter: permite definir el nombre del método getter, de la siguiente manera: getter=nombreDeMetodo
- readwrite: por defecto las propiedades en Objective C son de lectura escritura.
- readonly: para aquellas propiedades que se tienen que comportar como de sólo lectura.
- copy: permite realizar una copia del objeto cada vez que se asigne a otra variable.
- nonatomic: todas las variables de Objetive C son atómicas, en el sentido de comunicación entre hilos de ejecución. Es decir, significa que dos hilos intentan realizar un incremento sobre una variable, no haría problema a la hora de sincronizar esos dos hilos para que en ella se puedan actualizar los valores. Si queremos que las variables no se comporten así las definiremos como nonatomic. Al no necesitar sincronizar los accesos a las variables el rendimiento de la aplicación mejor.
- strong: serán punteros responsables o poseedores de una variable. Por lo tanto si se elimina ese puntero y no hay otra variable strong sobre ese conjunto de datos, se procederá a una liberación automática de la memoria que ocupaba ese dato. Se solía llamar antes retain. Sólo funciona con objetos. Más información: Referencia de Apple.
- weak: es el caso contrario a strong, no pasa nada si se libera una variable weak. Se pueden llamar también punteros temporales.
Ejemplo de propiedad que es una cadena de caracteres:
@property (nonatomic, strong) NSString *propiedadcadena;
Uso de propiedades de un objeto
La comparación con Java de la anterior sentencia es la definición de un atributo publico. Por lo que podría usarse como tal después de inicializar el objeto, incluyendo el código en el main.m lo siguiente:
como puede verse es una utilización más sencilla del atributo muy similar a Java. Primero se pone el nombre del objeto seguido de un punto y el nombre del atributo o propiedad como se dice en Objective C. Un igual y un valor y ya tenemos una asignación de un valor a una propiedad del objeto, que por supuesto podemos sacarla por la consola:
NSLog(@”%d”, objeto.propiedad);
Getters y Setters, paso de mensajes relacionados con una propiedad
Una vez definida la propiedad podremos hacer uso automático de los setter y getter de la siguiente manera, empezando por el setter:
De las misma manera que ejecutamos métodos de un objeto, o paso de mensajes en Objective C, ponemos el nombre del objeto, un espacio, set y el nombre de la propiedad un dos puntos para pasar un parámetro, ojo no flipes demasiado, y luego el valor o variable que le queremos pasar todo por supuesto rodeado de corchetes.
Funciona de una manera similar a un JavaBean, el nombre siempre es set en minúscula seguido del nombre de la variable con la primera letra mayúscula y el resto en minúsculas, CamelCase otra vez.
Todos los métodos en Objective C se escriben de esta manera, así son más fáciles de recordar y escribir.
Con el anterior ejemplo hemos colocado el valor 1 en la propiedad del objeto.
Veamos ahora el getter:
[objeto propiedad]
de esta manera ponemos entre corchetes el nombre del objeto, un espacio y el nombre de la propiedad.
A mi me sigue pareciendo más cómodo el uso del punto, pero para aquellas propiedades que sean privadas será más apropiado llamar al getter en su lugar.
La función nos devolverá el valor de la propiedad, otra diferencia con los JavaBeans. Aquí no se pone el get delante del nombre de la propiedad.
De esta manera para sacar por consola el valor almacenado en la propiedad pondremos lo siguiente:
NSLog(@”%d”,[objeto propiedad]);
Métodos de Clase
Inclusión de un método
Como en C++ será necesario incluir la definición del método en el fichero de cabecera .h, veamos un ejemplo sencillo:
Como podemos ver ponemos un – seguido de entre paréntesis el tipo del dato que se devuelve, en este caso void (nada) seguido del nombre de la función diAlgo y finalizando un ;
Implementación
A continuación deberemos realizar la implementación del método en el fichero .m, de una manera similar a la siguiente:
– (void) diAlgo{
//Aquí va el código del método
}
La implementación del método es igual a la definición salvo por los {} que son los que incluirán el código que deberá ejecutar el método al ser invocado.
Haremos que por ejemplo escriba algo en la consola:
NSLog(@”Algo”);
De esta manera el método completo quedará de la siguiente manera:
– (void) diAlgo{
//Aquí va el código del método
NSLog(@”Algo”);
}
Invocación
Para invocar al método será tan sencillo como lo hemos realizado anteriormente con el método alloc, pero esta vez escribiremos:
Así al ejecutarlo veremos que escribe por consola algo similar a esto:
2014-03-10 06:20:43.356 01_clases[6043:303] Algo
Paso de Parámetros
De cara a pasar parámetros es una de las cosas más confusas al principio de aprender Objective C debido a su sintaxis, veamos un ejemplo con una definición en el .h:
– (void) incremento:(int)inc;
Como podemos ver la definición es prácticamente la misma, salvo por el parámetro.
Después del nombre del método ponemos un : seguido del tipo del dato del parámetro entre (), en este caso un número entero, seguido del nombre interno de la variable del parámetro, en este caso inc y por supuesto finalizado por un ;
En este método intentaremos realizar un incremento de la propiedad con el valor que nos pasen como parámetro.
En el fichero de implementación ocurrirá algo similar:
– (void) incremento:(int)inc{
_propiedad+=inc;
}
La definición dela función será igual que en el fichero de cabecera salvo por los {}
Acceso a propiedades desde la implementación de la clase
Y dentro de los paréntesis incluimos el código de la implementación:
Como puede verse si queremos tener acceso a ls propiedades definidas en la clase, deberemos anteponer al nombre de la propiedad un _
También podríamos hacer lo siguiente:
self es lo más parecido al this de Java. Se refiere al objeto de la clase, seguido de un punto para acceder a la propiedad. En este caso estamos haciendo uso de un operador de asignación e incremento
+= para incrementar la variable con el valor de inc.
Como puede verse es un poco engorroso en comparación con Java al tener que incluir el _ o el self
Pero hay una posibilidad para simplificar el proceso, incluyendo lo siguiente en la implementación de la clase:
De esta manera podremos acceder directamente a la propiedad simplemente utilizando su nombre en cualquier parte de a implementación:
Resumen de código
Resumiendo los ficheros quedarían de la siguiente manera, empezando por el .h:
#import <Foundation/Foundation.h>
@interface MiClase : NSObject
@property int propiedad;
– (void) diAlgo;
– (void) incremento:(int)inc;
@end
Y el fichero .m:
#import “MiClase.h”
@implementation MiClase
@synthesize propiedad;
– (void) diAlgo{
//Aquí va el código del método
NSLog(@”Algo”);
}
– (void) incremento:(int)inc{
propiedad+=inc;
}
@end
Invocación del método con un parámetro
De esta manera con el método definido e implementado sólo haría falta invocarlo desde el main.n, sobre el objeto ya inicializado:
Se coloca el nombre del objeto, un espacio, el nombre del método, : y el valor o variable que se le quiere pasar por parámetro, todo metido entre [] y un ; al final.
Al intentar sacar el valor de la propiedad por consola veremos que se ha incrementado su valor en 3:
NSLog(@”%d”,objeto.propiedad);
Devolviendo valores
Para que pueda ser un función completa deberíamos ser capaces de devolver un valor para ello será necesario indicar el tipo de la variable a devolver entre los () de a definición y la implementación:
– (int) suma:(int)sumando;
y la implementación:
– (int) suma:(int)sumando{
return propiedad+sumando;
}
Como puede verse utilizamos con el C la palabra reservada return para indicar que queremos termine la ejecución del método y queremos devolver un valor.
Para recoger el valor basta colocar una sentencia se asignación, donde en la parte de la izquierda colocamos la variable donde queremos almacenar el valor devuelto, en el ejemplo, lo incluimos en el main.m:
Varios parámetros devolviendo un valor
Para poner un ejemplo más completo veremos un método que devuelve un valor y al que se le pasa más de un parámetro, en el ejemplo serán dos parámetros, empecemos por el fichero de cabecera:
– (int) suma:(int)sumando withInt:(int)otro;
Como se puede ver se separa por un espacio se indica un nombre complementario a la función indicando el tipo del dato que se le pasa, de manera informativa, luego se pone un :, entre paréntesis el tipo del dato, en este caso un entero y el nombre de la variable interna del método, antes del ;
Veamos la implementación en el fichero .m:
– (int) suma:(int)sumando withInt:(int)otro{
return propiedad+sumando+otro;
}
Con la misma cabecera y con el return de la suma de la propiedad con los parámetros.
Sólo faltaría la invocación del método desde el objeto en el main.m:
int k=[objeto suma:10 withInt:10];
Metodos estáticos
Los métodos estáticos son aquellos que no necesitan tener inicializado un objeto de la clase para poder invocarlos, en Java son los métodos static.
Para definir un método estático, se diferencia por el uso del + antes de la definición del método en vez del -, veamos un ejemplo, definido en el .h:
+ (void)estatico;
Ahora su implementación en el .m:
+ (void)estatico{
NSLog(@”Estático”);
}
Un método sencillo 🙂
Ahora su invocación desde el main.m:
[Clase estatico];
Como puede verse se utiliza el nombre de la clase en vez del nombre del objeto, al como ocurre con el método alloc.
Llamar a los métodos del padre (herencia)
En un determinado momento nos puede llegar a interesar llamar en vez de a la implementación de un método hecho en la clase que estoy creando nueva, llamar al método de mi clase padre, para ello deberemos usar lo siguiente:
[super nombreDelMetodo];
Llamar a los métodos de la propia clase
En un determinado momento nos puede llegar a interesar llamar a un método hecho en la clase que estoy creando nueva, para ello deberemos usar lo siguiente:
[self nombreDelMetodo];
Contructores (init)
Si queremos inicializar las propiedades de un objeto cuando se iniciativa es necesario crear una implementación del método init en la clase.
Dentro del fichero de cabecera deberemos introducir lo siguiente:
– (id) init;
Una vez definida el método debemos realizar la implentación, en el fichero .m, de una manera similar a la siguiente:
– (id)init {
self = [super init];
if (self) {
//Aquí se inicializan las variables
propiedad=0;
}
return self;
}
Como puede verse en el ejemplo el método devuelve self es decir el mismo objeto. Es una buena práctica llamar al constructor del padre antes de nada y luego comprobar si el objeto se ha inicializado apropiadamente con un if. Si eso es así, dentro del código del if, introducimos las inicializaciones pertinentes.
Contructores con parámetros (initWith)
Es posible que nos pueda interesar crear un constructor con parámetros, para ello llamaremos al método initWith y algo más en el nombre indicativo de que queremos hacer un init con algo más indicativo del tipo de datos que vayamos a utilizar en la inicialización. Esto se hace para que cuando se vaya a utilizar este método en la inicialización de un objeto, Xcode filtre los distintos constructores y ayuden al programador a escribirlos.
Por lo demás será un método normal, como los que hemos mencionado anteriormente. En el fichero .h escribiremos la definición:
– (id) initWithInt:(int)i;
Y en el fichero de implementación, .m, lo siguiente:
– (id) initWithInt:(int)i{
self = [super init];
if (self) {
propiedad=i;
}
return self;
}
Para poder llamar a ese contractos en el main.m pondríamos algo similar a esto:
MiClase *objeto2=[[MiClase alloc]initWithInt:3];
NSLog(@”%d”, objeto2.propiedad);
Otras maneras de inicializar propiedades
Si bien el uso de constructores está bastante extendido en la comunidad de programadores, hay otras maneras de inicializar las propiedades. La más sencilla es la sobrecarga del setter, veamos un ejemplo:
– (NSObject *)propiedad {
if (!propiedad) {
propiedad = [[NSObject alloc] init];
}
return propiedad;
}
De esta manera conseguimos que cada vez que se va a acceder a dicha propiedad se inicialice el objeto en el caso de que no haya sido inicializado previamente.
Visibilidad de métodos y propiedades
Por defecto todos lo métodos y propiedades incluidos en el fichero de cabecera serán públicos, pero podemos incluir métodos y propiedades privadas en la implementación de la clase. Veamos un ejemplo de inclusión de una propiedad privada, en el fichero .m:
@implementation MiClase{
@private int privada;
}
@end
De esta manera conseguimos que dicha propiedad no sea visible desde la invocación del objeto, pero podemos seguir utilizándola desde cualquier método de la clase simplemente por su nombre:
Para incluir un método privado basta con no incluirlo en el fichero de cabecera y sólo incluir el método en el fichero de implementación:
– (void) privado{
NSLog(@”desde el método privado”);
}
Resumen
Hemos visto cómo definir Clases, con sus propiedades y métodos, así como la declaración, inicialización de un objeto y el acceso a sus propiedades y métodos.
Ejercicios propuestos
- Crea un nuevo proyecto
- Crea una nueva Clase llamada MiClase
- Declara un objeto de la clase MiClase en el main.m
- Inicializa dicho objeto.
- Incluye una propiedad en la clase
- Utiliza esa propiedad desde el main.m
- Crea un método nuevo al que no le pases ningún parámetro ni devuelva nada en la clase
- Invoca ese nuevo método desde el main.m
- Crea un método nuevo al que le pases un parámetro pero no devuelva nada en la clase
- Invoca ese nuevo método desde el main.m
- Crea un método nuevo que haga la suma de dos números y devuelva el resultado
- Invoca ese nuevo método desde el main.m
- Crea una clase Cliente: con los siguientes datos: código de cliente, nombre, tlf y dirección.
- Inicializa un objeto de tipo Cliente en el main.m
- Crea un constructor con parámetros y sin parámetros para la clase Cliente
- Utiliza el constructor con parámetros para inicializar un objeto de la clase Cliente en el main.m