Application Life Cycle
En este tutorial vamos a pegar un repaso funcional de los elementos que componen el ciclo de vida de una aplicación iOS, haciendo hincapié en algunas de las opciones más habituales. Si bien el protocolo UIApplicationDelegate es más extenso, los métodos que se incluyen en la mayoría de los templates son más que suficientes para los casos más habituales.
Ficha técnica

Application Life Cycle
Código: 201111171406
Nivel: medio
Antes de empezar…

Las aplicaciones nativas de iOS se desarrollan en Cocoa Touch. Cocoa Touch utiliza Objective-C como lenguaje de programación. Objective-C es un superset de ANSI C… No es difícil llegar a la conclusión de que una app comenzará su andadura en la función main().

El problema es que, en iOS, la función main() no es el lugar donde comenzamos a trabajar. De hecho no es un lugar donde trabajamos habitualmente. El error está en pensar que nosotros trabajamos sobre la aplicación (Una instancia de la clase UIApplication), cuando lo que realmente hacemos en iOS es diseñar una clase capaz de trabajar como UIApplicationDelegate.

Para entender todo esto, es interesante revisar el ciclo de vida de la aplicación.

NOTA: Las primeras versiones de iOS y algunas versiones posteriores NO permiten la ejecución de aplicaciones en segundo plano, con lo que el ciclo de vida sería ligeramente distinto.

Para este ejemplo, supongamos que partimos de un sistema en el que nuestra aplicación no ha sido ejecutada. Esto es, no está en segundo plano.

Tap!

Al hacer tap sobre el icono de la aplicación, ocurren dos cosas. La primera es que el sistema muestra un gráfico de transición ( Relacionado con el Default.png, que vimos en otro tutorial… ). La segunda es que se llama a la función main().

main()
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Este ejemplo es de iOS 5. Lo que hace @autoreleasepool{…} es generar un espacio para utilizar cuando se use el método autorelease. Cocoa NO crea un espacio nuevo cada vez que utilizamos autorelease, simplemente usa el último espacio que se creó para el hilo en el que se encuentra la llamada. De esta manera tenemos un espacio definido para toda nuestra aplicación en el mainthread.

En versiones anteriores se creaba el objeto NSAutoreleasePool de manualmente (me refiero a que se hace con un init y un alloc, no a que lo haga el programador… ya viene en la plantilla) y se hacía release del mismo al terminar el programa.

Dentro del bloque @autorelease se encuentra una llamada a UIApplicationMain, lo que destacamos es el último parámetro, que definirá la clase de la instancia que será delegado de aplicación. En este caso, AppDelegate es la clase que hemos programado para nuestra aplicación.

En este momento, se generará la instancia de nuestra clase principal y se procesará el archivo plist que definirá aspectos como si la inicialización se debe hacer a partir de un archivo NIB o de un Storyboard.

En cualquier caso, lo que nos interesa es que transcurrido un tiempo tendremos una serie de instancias cargadas y configuradas. Y es entonces cuando la aplicación nos avisará (la clase que hemos programado está asignada como delegado…) de que todo está correcto.

application:didFinishLaunchingWithOptions:

En este momento, todo está cargado y podemos proceder a personalizar algunos aspectos clave como modificaciones programáticas de los controles a mostrar, recuperación de valores por defecto, etc. Es el primer lugar en que comenzamos a actuar programáticamente sobre el interfaz de la aplicación.

Si se trata de una aplicación que utiliza Storyboard, este método está vacio. Si se trata de una aplicación basada en archivos NIB, en el templare aparece algo parecido a esto...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[
self.window makeKeyAndVisible];
return YES;
}
De donde podemos destacar la línea en la que se llama al metodo makeKeyAndVisible, que es la que hace que la ventana de la aplicación se muestre.

Desde este punto entraremos en lo que se llama un bucle de control de eventos o event loop.

Event Loop

La aplicación inicia un bucle de control de eventos en el que se crea un espacio para los autorelease, se espera un evento, se procesa, se vacía el espacio de autorelease y se vuelve a empezar.

Además de las entradas de usuario como eventos táctiles podemos considerar otros dos casos. Uno de ellos es que el usuario presione el botón de Home, con lo que se lanza un evento que puede llevarnos a distintos subcasos. Por ejemplo, su el programador ha definido el parámetro UIApplicationExitsOnSuspend en el property list, el hecho de pulsar el botón Home hará que la aplicación termine. En ese caso el delegado recibirá el mensaje applicationWillTerminate. Esto mismo ocurriría también si el sistema no fuese compatible con la multitarea para los programas de terceros. (Por ejemplo, en versiones viejas de iOS o en iPhone 3G, independientemente de la versión).

Lo habitual, en cambio, es que se mande el mensaje applicationWillResignActive. Ya sea porque no se ha definido el parámetro que comentábamos en el párrafo anterior y el sistema soporte multitarea o porque se ha producido un evento de sistema como la recepción de una llamada o una notificación externa. En el caso de que sea porque el usuario ha decidido que la aplicación pase a segundo plano, la aplicación enviará a su delegado el mensaje applicationDidEnterBackground. Lo mismo ocurre si el sistema ha enviado una petición de algo y la hemos aceptado. En cambio, si la petición es enviada por el sistema y la cancelamos, se enviará automáticamente el mensaje applicationDidBecomeActive. Esto es muy importante de considerar porque applicationWillResignActive y applicationDidBecomeActive son funciones "contrarias". Es decir, lo que se hace en la primera debería deshacerse en la segunda.

Background

Si el mensaje que se ha enviado es applicationDidEntreBackground, la aplicación está en segundo plano. Esto implica que podría haberse configurado para hacer uso de alguno de los servicios que proporciona el sistema bajo el concepto multitarea. En cualquier caso, la ejecución de código en segundo plano no es del todo recomendable (aunque a veces es imprescindible) puesto que repercutirá en el rendimiento del sistema completo.

Foreground

Al volver a primer plano, ya sea porque volvemos a hacer tap en el icono de la aplicación (tanto en el springboard como en la lista de aplicaciones en segundo plano), la aplicación envía a su delegado el mensaje applicationWillEnterForeground (contrario al applicationDidEntreBackground) y seguidamente se envía el mensaje applicationDidBecomeActive (contrario al applicationWillResignActive).

Event Loop

En este punto, volvemos a estar en el Event Loop.

Lo habitual es que la aplicación no se pueda cerrar desde su interfaz. Cuando las aplicaciones están en segundo plano, deberían haber guardado todos sus datos y es desde el menú de aplicaciones en segundo plano desde donde podemos cerrar la aplicación. Cuando una aplicación se va a cerrar, se manda el mensaje applicationWillTerminate, pero NO se espera a que lo que hay implementado se complete, así que hay que tener cuidado con lo que queremos que haga.

Por último, notar que al salir de la aplicación, se vaciará el espacio de autorelease que habíamos creado.