Como hacer debug a errores EXC_BAD_ACCESS en X-Code

0

Te recomiendo que si eres nuevo en objective-c o no tienes claro el manejo de memoria, leas el Capítulo I: Objective-C. Lección 5: Manejo de Memoria.

A los que habíamos programado en otros lenguajes como c o c++, java, etc., al llegar a Objective-C es traumante encontrarse con este error y no saber por qué carajos sucede, bueno, sucede generalmente porque liberaste (release) algo que no deberías o antes de que deberías, o no lo liberaste, o sea algún retain de más o de menos.

Cuando tienes algún error de este tipo el programa terminará y el xcode te mandará un mensaje que dice algo como:

Program received signal:  “EXC_BAD_ACCESS”.

Pero para saber dónde está el error, es dificil si tienes códigos muy grandes, para eso existe una cosa llamada NSZombie, no, no se trata de Residen Evil, ni tu programa te comerá los sesos, significa que los objetos que han sido liberados (su retain count ha llegado a 0) se conserva una copia vacía, que se le llama zombie, cuando el código llama al zombie, este te advertirá que estas llamando a una instancia que ya ha sido liberada.

¿Cómo habilito los objetos NSZombie?

Abre el proyecto en el que quieres habilitar los zombies, y ubicate sobre el ejecutable del proyecto, abre el menú contextual y selecciona Get Info, luego Ve a la pestaña de Arguments, agrega en el fondo la variable NSZombieEnabled y pon el valor en YES

Get info

1. Abre get Info

2. Argumentos

3. Agrega NSZombieEnabled

Ahora corremos de nuevo el programa, solo que ahora, cuando el programa se interrumpa, no terminará, sino que se pausa, y podemos ver que manda un mensaje como el siguiente:

2011-01-27 20:11:40.434 backer[20102:810b] *** -[UISearchBar respondsToSelector:]: message sent to deallocated instance 0x661e850

Ahora lanzamos el debugger ve al Menú Run -] Debugger:

Pila de llamadas

En negro se muestran los métodos que están en tu código, en gris se encuentran las llamadas a métodos que se encuentran en alguna libería, entonces examinando los metodos en negro, puedes rastrear en donde estubo el objeto que liberaste más veces de las que deberías.

No olvides desactivar el zombie una vez que hayas terminado tu debug!

Saludos!

Capítulo I: Objective-C. Lección 5: Manejo de Memoria.

6

El manejo de memoria es muy importante, sobre todo en dispositivos portátiles como el iPhone y el iPod, que no cuentan con los recursos tan grandes que tienen las computadoras de escritorio de ahora. Este tema es por mucho uno de los más importantes, entre otras razones, si haces un buen manejo de memoria, evitarás bugs, leaks, y crashes, que son una verdadera molestia y algunos son difíciles de debuggear.

En las lecciones anteriores ya he realizado algunas actividades del manejo de memoria, pero es un tema tan importante que merece su propia lección.

Retain y Release.

El contador de retain (retain count) se utiliza para llevar un registro de las referencias activas hacia un mismo objeto. Todos los objetos tienen internamete este contador, de tal manera que si el contador de retain es mayor a cero, significa que aún esta siendo utilizado en alguna parte del código, pero una vez que llega a cero, se debe liberar la memoria que estaba usando.

Cuando se quiere agregar una referencia al objeto se llama al método retain: [objeto retain]; esto aumenta el contador de retain  en 1 y cuando deseamos remover una referencia llamaremos al método release: [objeto release]; esto disminuye el contador de retain en 1. Una vez que el contador llega a 0 (cero), se libera la memoria automáticamente con una llamada a [self dealloc]. Los métodos retain y release estan definidos en NSObject.

...
-(void) dealloc {
   //este método es llamado automáticamente cuando retain=0
   //no debe ser llamado manualmente
   [super dealloc];
}
...

Algunas reglas generales son:

  • Debes liberar con release todo lo que tu creaste, todo lo que es creado con alloc, o new, o los métodos que contienen la palabra copy (newObject, copyWithZone, mutableCopy,…), también debes darle release, si manualmente diste un retain.
  • En otras palabras debe haber un (ni mas ni menos) release por cada retain, alloc, new o copy.
  • Para liberar la memoria puedes usar tanto release, como autorelease, la diferencia es que release, disminuye el contador de retain inmediatamente, mientras que autorelease lo hará en un momento en el futuro.
  • Un objeto que es recibido como retorno de algún método no debe ser liberado en el método receptor, por lo general esta garantizado que sea válido mientras dure el método que lo recibe e incluso puede ser devuelto al método que lo llamó. Aunque existen algunas excepciones como algunas aplicaciones multihilo, donde una combinación de retain y release o autorelease asegurarán que el objeto se mantenga válido mientras sea necesario.
  • Si deseas conservar un objeto en una variable de instancia debes darle retain o copy, generalmente se hace a través de accesadores. (ver más abajo).
#import
#import

int main( int argc, const char *argv[] ) {
    NSString *uno =[[NSString alloc] initWithCString: "Uno"]; //al momento de crearlos tienen retain count=1
    NSString *dos =[[NSString alloc] initWithCString: "Dos"]; //retain=1
    NSString *tres =[[NSString alloc] initWithCString: "Tres"]; //retain=1

    printf( "Retain count uno: %d\n", [uno retainCount] ); //1
    printf( "Retain count dos: %d\n", [uno retainCount] ); //1
    printf( "Retain count tres: %d\n", [tres retainCount] ); //1

    //Si agregamos las cadenas a un array, el array agregara 1 al retain count para asegurarse
    //que los objetos sigan siendo válidos, serán liberados automáticamente cuando array sea liberado
    NSArray *array=[NSArray arrayWithObjects: uno, dos, nil]; //uno++;dos++;

    [dos release];//dos-- si libero la memoria de dos, el objeto sigue siendo válido ya que fué asegurado por array

    printf( "Retain count uno: %d\n", [uno retainCount] ); //2
    printf( "Retain count dos: %d\n", [uno retainCount] ); //1
    printf( "Retain count tres: %d\n", [tres retainCount] ); //sigue siendo 1 ya que no fue agregado al array

    [uno release]; //uno--
    [tres release]; //tres--
    //dos no debe ser liberado otra vez, ya fue liberado una vez.

    //en este punto uno y dos siguen siendo válidos, ya que array sigue siendo válido
    printf( "Retain count uno: %d\n", [uno retainCount] ); //1
    printf( "Retain count dos: %d\n", [uno retainCount] ); //1
    //printf( "Retain count tres: %d\n", [tres retainCount] ); //esta instancia ya no es válida

    return 0;
}

¿Cuándo hacer y cuando no hacer release?

Se debe llamar a release siempre cuando se tenga responsabilidad del objeto y ya no se necesite la referencia: Ej:
Correcto:

-(void) llamadoCorrectoDeRelease {
    NSArray *arregloUno=[[NSArray alloc] init]; //como se llamó a alloc, es reponsabilidad de este método liberarlo en su momento
    NSArray *arregloDos=[NSArray array]; //devuelve un arreglo que NO es responsabilidad de este método
    NSArray *arregloTres=[arregloDos copy]; //devuelve una copia (no profunda) del objeto en arregloDos, responsabilidad del método

    //Uso de los objetos....

    //Liberacion
    [arregloUno release]; //responsable por alloc
    //[arregloDos release] // NO se debe llamar a release, no es responsabilidad del método, y si se libera causará una excepción
    [arregloTres release]; //responsable por copy
}

¿Cuándo hacer y cuando no hacer autorelease?

Se debe llamar a autorelease cuando se tenga responsabilidad del objeto y pero se necesite la referencia en un nivel superior de la pila de llamadas: Ej:
Correcto:

-(NSArray) llamadoCorrectoDeAutoRelease {
    NSArray *arregloUno=[[NSArray alloc] init]; //como se llamó a alloc, es reponsabilidad de este método liberarlo en su momento
    NSArray *arregloDos=[[NSArray alloc] init]; //como se llamó a alloc, es reponsabilidad de este método liberarlo en su momento

    //Uso del array....

    //liberación
    [arregloDos release]; //después de este punto arregloDos ya no es referencia válida

    //Se debe liberar ya que fué creado con alloc, pero aún se quiere usar en metodoInvocador, se usa autorelease y se devuelve al invocador
    return [arregloUno autorelease]; //responsable por alloc
}
-(void) metodoInvocador {
    NSArray *objetoAutoLiberado=[self llamadoCorrectoDeAutoRelease];

    //se puede usar objetoAutoLiberado con seguridad y no debe ser liberado aquí, ya que es responsabilidad de llamadoCorrectoDeAutoRelease
    //[objetoAutoLiberado release]; //llamar a release aquí es INCORRECTO!, ya se ha llamado a autorelease, no es necesiario hacer más
}

Al llamar a autorelease, se te garantiza que la referencia será válida incluso si la devuelves al método invocador,

Propiedades, accesadores (o accesores?) y notación de punto.

Las propiedades y accesores (o accesadores, como sea, jeje), tienen algunas particularidades en objective-c y son de mis características favoritas, ya que ayudan al encapsulamiento y le dan un estilo bastante elegante.

¿Que son accesadores (accessors)? Son métodos para leer y escribir propiedades (variables de instancia) de un objeto. El método para escribir, se le llama Setter, y al método para leer se llama Getter, y son definidos automáticamente por objective-c:

//Persona.h
@interface Persona : NSObject {
    NSString *nombre;
}
@property (nonatomic, retain) NSString *nombre;
-(void) usoDeAccesador;
@end
//Persona.m
#import "Persona.h"
@implementation Persona
@synthetize nombre;

-(void) usoDeAccesador {
    //Setter
    [self setNombre:@"Pedro"];
    //Getter
    NSLog(@"%@", [self getNombre]);

    //Método abreviado con "." (punto)
    self.nombre=@"Yo no me llamo Javier"; //setter
    NSLog(@"%@", self.nombre); //getter
}
-(void) dealloc {
    self.nombre=nil;

    // también es posible usar lo siguiente, pero uno solo de los dos!
    //[self release];
    //creo que es mejor usar la opción primera (self.nombre=nil), porque se asegura que no envíes mensajes de manera accidental a una instancia inválida
}
@end
  • A través del accesador podemos obtener acceso a las variables de instancia, tanto para escribir como para leer.
  • Para definir los accesadores debemos declararlo primero con: @property (modificadores) TipoDeLaVariable *nombreDeVariable;
  • @synthetize se usa para que objective-c implemente automáticamente los métodos.

Existen diferentes modificadores para las propiedades:

  1. readonly: muy claro, indica que solo se creará el método para leer, y no para escribir.
  2. atomic / nonatomic: atomic indica que se implementará una cerradura para impedir que múltiples métodos (en programas multihilo) accedan a la propiedad, nonatomic no implementa la cerradura.
  3. assign: Se implementa el método de setter de tal manera que solo se asigna: seria equivalente a algo como: -(void) setPropiedad: (id)valor { propiedad=valor; } es decir una asignación simple.
  4. copy: Si se especifica copy, el método setter, se implementará haciendo una copia del objeto asignado, al hacer esto se debe liberar con release ya que fué copiado (copy)
  5. retain: Al igual que con copy, retain aumenta el contador de retain en 1, por lo que debe ser liberado con release cuando sea adecuado. ¿Cuándo es adecuado?, sigue leyendo. La implementación que objective-c hace automáticamente, sería parecida a la siguiente:
    -(void) setPropiedad: (id)valor {
    if (propiedad=nil) { [propiedad release]; }
    propiedad=[valor retain];
    }
    .
  6. Si se asigna un nuevo objeto a una propiedad marcada con retain, se libera cuando se asigna el nuevo objeto, pero, debes entender que no ocurre automáticamente sino cuando le asignas un nuevo objeto, por lo que el viejo es liberado, pero el nuevo no será liberado hasta que tu lo indiques. Lee a continuación:

Si utilizo copy o retain en la propiedad, ¿cuándo debo llamar a release? Dealloc!!

Depende de cada programa, pero sigue las mismas reglas, se libera cuando ya no se necesite, generalmente se libera cuando el objeto contenedor no es necesario, para esto utilizaremos el método especial dealloc.

El método dealloc es invocado automáticamente cuando el objeto va a morir, entonces ahí podemos liberar las propiedades que fueron marcadas con retain y copy.

Capítulo I: Objective-C. Lección 4: Categorías.

0

Ir a Lección 3: Herencia.

Las categorías son una característica muy util de Objective-C, muy útiles cuando quieres agregar funcionalidad a una clase, pero no quieres reescribirla o no tienes el código fuente como cuando se encuentra en una biblioteca.
Supongamos que tenemos una clase Perro con la siguiente declaración y definición:

//  Perro.h
@interface Perro : NSObject {
}
-(void) ladra;
-(void) corre;
@end
//  Perro.m
#import "Perro.h"
@implementation Perro
-(void) ladra {
        //Guau guau
}
-(void) corre {
        //trut trut turut
}
@end

Se puede agregar métodos a una clase existente, aún si no tuvieramos su código fuente con una categoría:

//  PerroTrucos.h
@interface Perro (Trucos) {
}
-(void) traeLaVarita;
@end
//  Perro.m
#import "PerroTrucos.h"
@implementation Perro (Trucos)
-(void) traeLaVarita {
 //va por la varita
}
@end
  • Para crear una categoría, se debe declarar con @interface NombreDeClaseExistente (NombreDeCategoriaNueva)
  • Una vez declarada la categoría se define con @implementation NombreDeClaseExistente (NombreDeCategoriaNueva)
  • Solo se pueden agregar Métodos, no se aceptan variables de instancia.
  • Solo puede haber una categoría con el mismo nombre, pero se pueden agregar muchas categorías a una misma clase.

Capítulo I: Objective-c. Lección 3: Herencia

0

Ver lección 2: Métodos y miembros.

El tipo de datos id.

En objective-c existe un tipo de datos llamado id. que actúa de manera similar a void*, es decir, puede apuntar a cualquier tipo de objeto, por lo anterior, en objective-c cuando invocamos métodos no se requiere (no obligatoriamente) saber los tipos de los objetos, es suficiente con que el método exista, a esto se le llama pasar un mensaje.

//  Perro.h
@interface Perro : NSObject {
}
-(void) ladra;
-(void) corre;
@end
//  Perro.m
#import "Perro.h"
@implementation Perro
-(void) ladra {
        //Guau guau
}
-(void) corre {
        //trut trut turut
}
@end
//  Gato.h
@interface Gato : NSObject {
        -(void) maulla;
        -(void) corre;
}
@end
//  Gato.m
#import "Gato.h"
@implementation Gato
-(void) maulla{
        //miauuu miauu
}
-(void) corre {
        //silencio
}
@end
#include <stdio.h>
#import "Perro.h"
#import "Gato.h"
int main (int argc, const char * argv[]) {
        Perro *rintintin=[[Perro alloc] init];
        Gato *misifus=[[Gato alloc] init];
        id cuadrupedo:

        //métodos de cada clase
        [rintintin ladra];
        [misifus maulla];

        cuadrupedo=rintintin; //rintintin es instancia de Perro
        [cuadrupedo corre]; //trut trut
        //[cuadrupedo maulla]; //Genera excepción ya que Perro no implementa el método maulla

        cuadrupedo=misifus; //misifus es una instancia de Gato
        [cuadrupedo corre]: //silencio
        //[cuadrupedo ladra]; //Genera excepción ya que Gato no implementa el método ladra

        //liberar memoria
        [rintintin release];
        [misifus release];
    return 0;
}

Podemos invocar los métodos desde ambas clases cuando las asignamos a la variable cuadrupedo, que es tipo id, y por lo tanto no le importa si la instancia a la que apunta tiene implementado el método que se le pasa, ni hay que hacer castings, simplemente intentará enviar el mensaje a la instancia, y si el método no existiera, generaría una excepción.

Herencia.

La herencia en Objective-C es similar a java, se pueden sobreescribir los métodos simplemente escribiendo en la clase hija la nueva definición del método.

//  Mamifero.h
#import
@interface Mamifero : NSObject {
        int patas;
        char *nombre;
}
-(int) getPatas;
-(char*) getNombre;
-(id) initWithNombre:(char*)_nombre Patas:(int)_patas;
@end
//  Mamifero.c
#import "Mamifero.h"
@implementation Mamifero
-(int) getPatas {
        return patas;
}
-(char*) getNombre {
        return nombre;
}
-(id) initWithNombre:(char*)_nombre Patas:(int)_patas {
    self = [super init];
    if ( self ) {
        nombre=_parteReal;
        patas=_patas;
    }
    return self;

}
@end
//  Perro.h
#import
#import "Mamifero.h"
@interface Perro : Mamifero {
    char raza;
}
-(int) getRaza;
-(id) initWithNombre:(char*)_nombre Raza:(char*)_raza;
@end
//  Perro.c
#import "Perro.h"
@implementation Perro
-(int) getRaza {
        return raza;
}
-(id) initWithNombre:(char*)_nombre Raza:(char*)_raza {
    self = [super initWithNombre:_nombre Patas:4];
    if ( self ) {
        raza=_raza;
    }
    return self;
}
@end
#import "Mamifero.h"
#import "Perro.h"
#import
int main( int argc, const char *argv[] ) {
    Mamifero *mamifero = [[Mamiferoalloc] initWithNombre:"Juan Perez" Patas:2];
    Perro *perro = [[Perro alloc] initWithNombre:"firulais" Raza:"Rottweiler"];

   printf("\nNumero de Patas Mamifero %d",[mamifero getPatas]); //2
   printf("\nNumero de Patas Perro %d",[mamifero getPatas]); //4 asignado automaticamente

    [mamifero release];
    [perro release];
    return 0;
}

La herencia es parecida a Java, solo se puede tener una clase padre, y para sobreescribir los métodos, simplemente escribimos una nueva definición en la clase hija.

Saludos!

Capítulo I: Objective-C. Lección 2: Métodos y miembros.

0

Ver Lección 1: Conoce la sintaxis.

Niveles de acceso a miembros.

El nivel de acceso a miembros de una clase se especifica de manera similar a c++:

//Clase.h
@interface Clase : NSObject {
@private
        int miembroPrivado1;
        int miembroPrivado2;
@public
        int miembroPublico;
@protected
        int miembroProtegido;
}

//
//  Clase.m
#import "Clase.h"
@implementation Clase
@end

Los miembros públicos son utilizados de la siguiente manera:

#include <stdio.h>
#import "Clase.h"
int main (int argc, const char * argv[]) {
    Clase *c = [[Clase alloc] init];

    //Acceder a miembroPublico
    c->miembroPublico = 69;
    printf("Miembro Público: %d\n", c->miembroPublico );

    // No se puede acceder al miembroPrivado
    //c->miembroPrivado1 = 123;
    //printf("Miembro Privado: %d\n", c->miembroPrivado1 );

    [c release];
    return 0;
}

Métodos en Objective-C.

Ya vimos la declaración y definición de métodos en objective-c en la lección pasada, pero aún quedan algunos detalles por ver, en primer lugar veamos los métodos de Instancia y Métodos de Clase:

//  NumeroComplejo.h
#import <Cocoa/Cocoa.h>
@interface NumeroComplejo : NSObject {
        int parteReal;
        int parteImaginaria;
}
//Métodos de Instancia
-(void) initWithParteReal: (int)_parteReal ParteImaginaria:(int)_parteImaginaria;
-(void) sumarNumeroComplejo:(NumeroComplejo*)sumando;
-(void) restarNumeroComplejo:(NumeroComplejo*)substrayendo;
-(int) getParteReal;
-(int) getParteImaginaria;
//Métodos de Clase
+(NumeroComplejo*) sumarNumerosComplejos:(NumeroComplejo*)sumandoUno SumandoDos:(NumeroComplejo*)sumandoDos;
+(void) initialize;
@end

Los métodos de instancia solo pueden ser utilizados si se ha creado una instancia de la clase [instanciaDeNumeroComplejo sumarNumeroComplejo:otraInstancia], los métodos de clase pueden ser usados sin crear una instancia de la clase, se utilizan a través del nombre de la clase: [NumeroComplejo initialize]. Initialize es un método especial de objective-c, el cual es invocado automáticamente, en el momento que se utilice la clase por primera vez, no es necesario que lo llames.

Métodos con muchos parámetros:

Cuando un método requiere de más de un parámetro se utiliza la siguinte forma:

+      (NumeroComplejo*) sumarNumerosComplejos:(NumeroComplejo*)sumandoUno SumandoDos:  (NumeroComplejo*)sumandoDos;
//acceso (tipoDeRetorno)   nombreDeMétodo:       (tipoParametro1) parametro1 NombreParám2:(tipoParametro2) parametro2;

En este caso el Método llamado sumarNumerosComplejos es un método de Clase, que devuelve un NumeroComplejo* y toma dos parámetros NumeroComplejo*; algo que no existe ni en C/C++ ni java son los nombres de los parámetros, que no sirven para nada en el código, pero son muy útiles para el programador, ya que te dicen qué parámetro sigue.

Ahora veamos la implementación de la clase:

//
//  NumeroComplejo.m
#import "NumeroComplejo.h"
@implementation NumeroComplejo
-(void) initWithParteReal: (int)_parteReal ParteImaginaria:(int)_parteImaginaria {
        self = [super init];
    if ( self ) {
        parteReal=_parteReal;
        parteImaginaria=_parteImaginaria;
    }
    return self;
}
-(void) sumarNumeroComplejo:(NumeroComplejo*)sumando {
        parteReal+=[sumando getParteReal];
        parteImaginaria+=[sumando getParteImaginaria];
}
-(void) restarNumeroComplejo:(NumeroComplejo*)substrayendo {
        parteReal-=[sumando getParteReal];
        parteImaginaria-=[sumando getParteImaginaria];
}
-(int) getParteReal {
        return parteReal;
}
-(int) getParteImaginaria {
        return parteImaginaria;
}
//Métodos de Clase
+(NumeroComplejo*) sumarNumerosComplejos:(NumeroComplejo*)sumandoUno SumandoDos:(NumeroComplejo*)sumandoDos {
        NumeroComplejo *resultado=[[NumeroComplejo alloc]
                                                           initWithParteReal:[sumandoUno getParteReal] ParteImaginaria:[sumandoUno getParteImaginaria]]
        [resultado sumarNumeroComplejo:sumandoDos];
        return [resultado autorelease]; //libera memoria cuando ya no es usada automaticamente
}
+(void) initialize {
        //no necesito inicializar nada antes de usar la clase
}
@end

El método init.

-(void) initWithParteReal: (int)_parteReal ParteImaginaria:(int)_parteImaginaria;
  • El método init es parecido a los constructores de c++, en él, se asignan los valores iniciales de la instancia.
  • Verifica que alloc haya devuelto una instancia válida al compararla con nil, nil es el equivalente a NULL, pero nil es un objeto.
  • Para acceder al método del ancestro se utiliza super, como en [super init].
  • El constructor por defecto es -(id) init. El cual solo verifica que se haya creado la instancia, pero no inicializa valores.
  • En general los metodos init, en objective-c solo realizan operaciones de inicialización, y no otras operaciones como en java o c++.
#include <stdio.h>
#import "NumeroComplejo.h"
int main (int argc, const char * argv[]) {

        //2i+1
    NumeroComplejo *complejo1=[[NumeroComplejo alloc] initWithParteReal:1 ParteImaginaria:2];
        //2i+3
        NumeroComplejo *complejo2=[[NumeroComplejo alloc] initWithParteReal:3 ParteImaginaria:2];
        //4i+4
        NumeroComplejo *suma=[NumeroComplejo sumarNumerosComplejos:complejo1 SumandoDos:complejo2];
        //5i+4
        NumeroComplejo *complejo3=[[NumeroComplejo alloc] initWithParteReal:4 ParteImaginaria:5];
        //9i+8
        [suma sumarNumeroComplejo:complejo3];
        //7i+5
        [suma restarNumeroComplejo:complejo2];

        //liberar memoria
        [complejo1 release];
        [complejo2 release];
        [complejo3 release];
        //suma no es liberada ya que es liberada automaticamente en el método sumarNumerosComplejos
    return 0;
}

Bien eso es todo por esta lección, saludos.

Capítulo I: Objective-C. Lección 1: Conoce la sintaxis.

1

¿Qué es Objective-C?

En la teoría Objective-C es una extensión orientada a objetos (POO) de la sintaxis de C, no se trata en sí de otro lenguaje, sino que se trata del bien conocido y viejo amigo C, solo que se le han agregado muchas cosas interesantes y útiles. Si bien en un principio es cierto, la realidad es que debes acostumbrarte a las características de Objective-C.

@interface

La definición de la clase se realiza en el archivo de cabecera, que debe terminar en “.h”, como en C normal.

//MiClase.h
#import

typedef enum { rojo=0,azul,verde }Color;

@interface MiClase : NSObject {
/* Comentario estilo C: definición de miembros */
 int enteroA;
 int enteroB;
}

//Definición de métodos
-(int) getEnteroA:
-(void) setEnteroA:(int) _entero;
-(int) getEnteroB:
-(void) setEnteroB:(int) _entero;
-(int) multiplicaEnteros;
+(char*) nombreDeColor:(Color) color;
@end

¿Cómo se ve una clase en Objective-C? Para alguien que viene de Java o C/C++ se ve familiar, pero a la vez, se desconocen varias cosas, la primera, definición de clases:

  • Se comienza con la palabra reservada @interface.
  • NSObject: Es la abreviación de NeXTStep Object, es la clase mas abstracta que hay, todas las clases deben heredar de otra clase o de NSObject.
  • La herencia se indica con Clase : ClasePadre, como en MiClase : NSObject.
  • Las variables de instancia se declaran entre las llaves de @interface Clase : NSObject { … }.
  • El acceso por defecto es protected.
  • Después de los miembros de la clase se especifican los métodos, que tienen la forma: alcance (tipoDeRetorno) nombreDeMetodo:(tipoParametro1) parametro1:
  • El alcance de un método se especifica con un signo menos: – para métodos de instancia; y un signo más + para metodos de clase.
  • La definición termina con @end.

@implementation

La implementación de la clase se escribirá en un archivo con terminación “.m”:

//MiClase.m
#import "MiClase.h"
@implementation MiClase
-(int) getEnteroA
{
 return enteroA;
}
-(void) setEnteroA:(int) _entero
{
enteroA=_entero;
}
-(int) getEnteroB
{
 return enteroB;
}
-(void) setEnteroB:(int) _entero
{
enteroB=_entero;
}
-(int) multiplicaEnteros {
 return enteroA*enteroB;
}
+(char*) nombreDeColor:(Color) color
{
 switch(color) {
  case azul:
   return "Azul";
  break;
  case rojo:
   return "Rojo";
  break;
  case verde:
   return "Verde";
  break;
 }
return "Color Desconocido";
}
@end

La implementación de la clase

  • Se comienza con @implementation NombreDeClase.
  • La definición de los métodos se realiza de manera similar a como se hizo la definición en la cabecera de la clase.
  • Se termina con la palabra reservada @end.

Usando la clase.

Como ya mencioné, Objective-C es una extensión de C, y sabemos que todo programa de C, debe tener una función main:

#import
#import "MiClase.h"
int main( int argc, const char *argv[] ) {

    MiClase *instancia = [[MiClase alloc] init];

    //Debemos especificar los valores:
    [instancia setEnteroA:1];
    [instancia setEnteroB:12];

    //llamamos a los métodos de la instancia que devuelven valores
    printf("%d por %d es igual a %d",[instancia getEnteroA],
          [instancia getEnteroB], [instancia multiplicaEnteros] );

    //tenemos que liberar la memoria cuando ya no necesitamos la instancia
    [instancia release];

    //Pero podemos llamar a los métodos de la clase:
    printf("%s",[MiClase nombreDeColor:rojo]);
    return 0;
}
  • Las llamadas a métodos de una clase, de una manera equivalente a C++ instancia->metodo() se hace en la forma [instancia metodo];
  • Se puede hacer llamadas anidadas: [[instancia metodoQueDevuelveOtroObjeto] llamadaAMetodoDelObjetoDevuelto] .sería el equivalente a lo siguiente en C++: (instancia->metodoQueDevuelveOtroObjeto())->llamadaAMetodoDelObjetoDevuelto();
  • Para Crear instancias de una clase se usa MiClase *instancia=[[MiClase alloc] init];
  • A diferencia de C++, Objective-C siempre utiliza apuntadores para objetos de las clases.
  • Para obtener la instancia de la clase la llamada al Método alloc, es como usar malloc en c, solo reserva la memoria para la instancia.
  • La llamada al método init, efectúa al inicialización de la instancia, como un constructor.
  • Usualmente se efectua en una sola línea para crear e inicializar el objeto: MiClase *instancia=[[MiClase alloc] init];
  • Al igual que en las otras variantes de C, debes liberar la memoria obtenida con alloc, llamando al método release, que es heredado de NSObject.

Bien, eso es todo por hoy. Saludos.

Go to Top