Añade iAd en tu aplicación de forma sencilla

Después de un tiempo de inactividad (si es que alguna vez la hubo), voy a explicar como añadir iAd en una aplicación en unos sencillos pasos. Para quien no lo sepa, iAd es la plataforma publicitaria que apple presentó junto con la versión 4 de iOS. La novedad de iAd con respecto a otras plataformas como adMob (google), es la posibilidad de crear anuncios publicitarios sin necesidad de abandonar la aplicación. El funcionamiento es el siguiente, la aplicación carga la publicidad en el banner que el desarrollador de turno ha colocado en su aplicación, al tocar el banner, aparece una vista sobre nuestra aplicación, esta contiene la publicidad a pantalla completa. Aquí entra la novedad más importante para los anunciantes, la posibilidad de crear la publicidad a pantalla completa y además interactiva, utilizando tecnologías como javascript, CSS3 y HTML5.

Soltado el rollo, entremos en materia:

– Primero creamos un nuevo proyecto “View-based Application”.

– Añadimos al proyecto el framework iAd.framework.

– Ahora vamos al .h de nuestra clase principal, hacemos el import del framework e indicamos que vamos a implementar el protocolo ADBannerViewDelegate:

1
2
3
4
5
6
7
8
9
10
#import <UIKit/UIKit.h>;
#import <iAd/iAd.h>;
@interface iAdViewController : UIViewController <ADBannerViewDelegate>
{
      ADBannerView *adBanner;
      BOOL bannerIsVisible;
}
@property (nonatomic,assign) BOOL bannerIsVisible;
@end

– Ahora vamos al .m, descomentamos el metodo viewDidLoad y añadimos lo siguiente:

1
2
3
4
5
6
7
8
9
10
- (void)viewDidLoad {
      adBanner= [[ADBannerView alloc] initWithFrame:CGRectZero];
      adBanner.frame = CGRectOffset(adView.frame, 0, -50);
      adBanner.requiredContentSizeIdentifiers = [NSSet setWithObject:ADBannerContentSizeIdentifierPortrait];
      adBanner.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
      [self.view addSubview:adBanner];
      adBanner.delegate=self;
      self.bannerIsVisible=NO;
      [super viewDidLoad];
}

– En este punto el banner esta añadido 50 pixeles por encima de la pantalla. Haciendo uso del protocolo ADBannerViewDelegate capturaremos el momento en el que el banner carga la publicidad y en ese momento animaremos el banner para que baje 50px y sea perfectamente visible. Para ello implementamos los metodos bannerViewDidLoadAd (el banner se carga con exito) y didFailToReceiveAdWithError (cuando hay algún problema). Añadimos el siguiente código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
 if (!self.bannerIsVisible)
 {
  [UIView beginAnimations:@"animateAdBannerOn" context:NULL];
// movemos el banner 50px hacia abajo
  banner.frame = CGRectOffset(banner.frame, 0, 50);
  [UIView commitAnimations];
  self.bannerIsVisible = YES;
 }
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible)
 {
  [UIView beginAnimations:@"animateAdBannerOff" context:NULL];
// movemos el banner 50 px hacia arriba
  banner.frame = CGRectOffset(banner.frame, 0, -50);
  [UIView commitAnimations];
  self.bannerIsVisible = NO;
 }
}

Eso es todo con lo visto en este tutorial deberiamos poder ver un banner de prueba en nuestro View.

Es importante que coloquemos nuestro banner arriba o abajo y fuera de elementos que tengan scroll, para que el banner esté visible el mayor tiempo posible.

http://www.werkham.com/2011/02/anade-iad-en-tu-aplicacion/

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

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.

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

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.

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.