Développement sur iOS : la gestion de la mémoire (seconde partie)

ios-86-iphone-550x365
 Suite de la série d’articles consacrés au développement sur iOS. Sébastien Feldis (Pix ‘n Bits) continue son article sur la gestion de la mémoire sous iOS. Pour les retardataires, la première partie est par ici. Pour les autres, voici la suite :

Gestion de la mémoire sous iOS (épisode 2)

Qu’est ce qu’un autorelease pool ?

Un autorelease pool implémente un mécanisme destiné à simplifier la gestion du cycle de vie des objets Objective-C disposant d’un compteur de références. Il est constitué d’une file d’attente d’objets qui seront libérés dans un futur proche.

Pour ajouter un objet à la file d’attente d’un autorelease pool on lui envoie le message autorelease. Il faut savoir que c’est toujours l’autorelease pool créé le plus récemment par le thread courant qui effectue la collecte de l’objet demandeur.

Lorsqu’un thread meurt, il libère tous les autorelease pools qui lui sont associés.

S’il n’y avait pas d’instance d’autorelease pool allouée dans le contexte du thread courant au moment de l’envoi d’un message autorelease le debugger vous le fera savoir en vous affichant le message suivant :

*** _NSAutoreleaseNoPool(): Object 0xea1337 of class NSString
autoreleased with no pool in place - just leaking

Ceci signifie que l’objet n’a pu être collecté et qu’il a en conséquence généré une fuite mémoire.Pour libérer le contenu de la file des objets collectés par l’autorelease pool il suffit de lui envoyer le message drain ou release. L’autorelease pool commencera alors le parcours des objets de la file et leur enverra chacun le message release.

Une fois l’intégralité de la file parcourue il finira par se libérer lui-même.Il est important de noter qu’il est préférable d’envoyer le message drain pour libérer un autorelease pool car sous OSX en cas d’activation du garbage collector les messages release seront ignorés et donc l’autorelease pool ne sera pas informé qu’il peut procéder à la libération des objets qu’il a collectés.

Et dans la pratique

Rien de mieux qu’un petit exemple pour expliquer de quoi il retourne.

Vous l’avez peut-être déjà remarqué mais chaque programme Cocoa Touch dispose d’une fonction main dans laquelle est alloué un autorelease pool. C’est simplement une nécessité car il faut en instancier un afin qu’il puisse collecter les objets auxquels sont envoyés le message autorelease en attendant que la RunLoop du thread principal ait pu être créée.

#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool! *pPool=[[NSAutoreleasePool alloc] init];
for(int nCpt=0 ; nCpt<1000 ; nCpt++)
[[[NSString alloc] init] autorelease];
[pPool drain];
return 0;
}

l’exemple ci-dessus tous les objets de type NSString alloués sont ajoutés à la file d’attente de l’autorelease pool pPool.Le message drain est envoyé à pPool qui lancera la procédure de libération des objets contenus et de lui-même.

RunLoop et autorelease pool implicite

Le thread principal de votre application dispose d’une RunLoop.

Une RunLoop est une boucle chargée de traiter les événements provenant de différentes sources externes qui sont envoyés à un thread donné.

La RunLoop principale de votre application héberge un autorelease pool implicite qui est initialisé lors du démarrage d’une nouvelle itération de la boucle de la RunLoop et drainé lorsque la fin de l’itération de la boucle courante de la RunLoop est atteinte.

Pourquoi et quand déclarer un Autorelease pool explicite ?

Chaque thread supplémentaire instancié par l’utilisateur nécessite la déclaration d’un autorelease pool afin qu’il soit capable de collecter les objets autoreleasés faisant partie de son contexte.

On peut également instancier un autorelease pool au cœur d’une boucle de traitement dans laquelle de nombreux objets sont créés afin de réduire l’empreinte mémoire maximum consommée par l’application.

Dans l’exemple précédent les 1000 instances de NSString n’étaient libérées qu’une fois les 1000 itérations de la boucle achevées.

Dans l’exemple suivant chaque objet NSString alloué sera immédiatement libéré lors de l’achèvement de chaque itération de boucle.

#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
for(int nCpt=0 ; nCpt<1000 ; nCpt++)
{
NSAutoreleasePool *pPool=[[NSAutoreleasePool alloc] init];
[[[NSString alloc] init] autorelease];
[pPool drain];
}
return 0;
}

Utilisation et conventions liées aux autorelease pool

Le framework Cocoa Touch utilise intensivement les propriétés de l’autorelease pool afin de simplifier la gestion du cycle de vie des objets qu’il crée.

Afin d’éviter les erreurs liées à la gestion du cycle de vie des objets créés par le framework Cocoa Touch Apple a défini une convention qui permet d’instantanément savoir à qui revient la responsabilité de la libération d’un objet.L’utilisateur est responsable de la libération d’un objet s’il le crée par le biais d’un message commençant par alloc, new ou contenant copy :

NSString *pstr=[[NSString alloc] init];
NSString *pstrMutable=[pstr mutableCopy];
NSString *pstr2=[NSString new];

ou s’il lui envoie le message retain :

NSString *pstr3=[pstr2 retain];

Cette responsabilité implique que l’utilisateur doit envoyer le message release ou autorelease à l’objet en question une fois qu’il n’en a plus besoins :

[pstr release];
[pstrMutable release];
[pstr2 release];
[pstr3 release];

Toute autre situation implique que l’utilisateur n’est pas responsable de la libération de l’objet.La plupart des classes Cocoa Touch disposent de nombreuses méthodes renvoyant des objets autoreleasés :

[NSString stringWithFormat:@"%@",@"exemple"];
[NSMutableArray arrayWithCapacity:10];

Ces objets seront automatiquement libérés lorsque le message drain sera envoyé à l’autorelease pool créé le plus récemment. En général il s’agira de celui associé à la RunLoop du thread principal.

Si l’utilisateur souhaite conserver l’usage de ces objets en dehors du cycle de vie permis par l’autorelease pool courant il devra s’approprier l’objet en lui envoyant le message retain.

Voilà qui mets fin à ce second article, je vous donne rendez-vous au prochain épisode.

PARTAGER

  • Facebook
  • Twitter
  • Myspace
  • Google Buzz
  • Reddit
  • Stumnleupon
  • Delicious
  • Digg
  • Technorati

avatar
Auteur: Sébastien Voir tous les articles de
Sébastien Feldis Pixs'n Bits :: iPhone & iPad Games and Applications http://www.facebook.com/group.php?gid=187338674178 http://www.pixsnbits.com

Publier un commentaire

Vous devez être connecté(e) pour publier un commentaire.