La gestion de la mémoire (2)

27 11 2009 In: Cocoa, Objective-C

Dans le premier épisode, nous avons vu qu'un objet qui avait alloué la mémoire pour un autre objet était également responsable de la libérer lorsque l'objet ne lui était plus nécessaire. Ce système est similaire à l'allocation dynamique de la mémoire telle qu'on la connaît en langage C sous la formes des fonctions malloc() et free().

Cependant, ce système comporte un inconvénient majeur dès qu'un objet est passé d'un objet à un autre. Étudions la séquence suivante:

  • Un objet A alloue un objet B.
  • L'objet A passe l'objet B à un objet C. L'objet C garde un pointeur sur l'objet B dont il a encore besoin par la suite.
  • L'objet A n'a plus besoin de l'objet B. Il lui envoie donc un message -[release].
  • L'objet C tente d'appeler une méthode de l'objet B, ce qui mène à un plantage.

Il a donc fallu incorporer un système qui permette de conserver un objet en mémoire tant qu'il est nécessaire, et le libérer lorsqu'il ne l'est plus. Ce système est l'autorelease.

Autorelease pool

Examinons l'exemple suivant:

NSArray* monArray = [[NSArray alloc] init];
[monArray autorelease];

La première ligne alloue, comme nous l'avons vu la dernière fois, un objet de type NSArray et l'initialise. La deuxième ligne ajoute monArray à l'autorelease pool courant.

Un objet de type NSAutoreleasePool conserve une liste d'objets. Ces objets seront désalloués à la fin de la boucle d'événements.

La boucle d'événements

Le moteur d'exécution (runtime) est un programme qui offre une structure à l'exécution des programmes écrits en Objective-C. Il comporte une boucle d'événements:

  • Au début de la boucle, les événements (frappe clavier, mouvements de la souris, timers écoulés, messages des autres applications, etc.) sont récupérés.
  • L'application (votre code !) traite les événements.
  • Les autorelease pools sont vidés
  • Puis on revient au début de la boucle.

Ce principe permet de garantir l'existence de l'objet pendant l'exécution d'un itération de votre code.

Dans les applications Cocoa utilisant une interface graphique, un NSAutoreleasePool est instancié automatiquement pour le thread principal. En général, vous n'avez donc pas besoin d'en créer un. Vous serez toutefois amené à le faire si vous concevez une application multi-thread, ou si vous instanciez un grand nombre d'objets dont la durée de vie est limitée.

Retain/Release

La gestion de la mémoire en Objective-C est souvent expliquée par l'analogie suivante: un petit chien se met à courir dès qu'il n'a plus aucune laisse autour du cou. Tant qu'il a au moins une laisse au cou, il ne peut s'échapper. Évidemment, s'il a plusieurs laisses qui le retiennent, il ne peut pas non plus s'enfuir.

Envoyer un message -[retain] à un objet lui passe une laisse autour du cou.

Lui envoyer un message -[release] lui retire une laisse.

Concrètement, chaque objet héritant de NSObject possède une variable d'instance retainCount. Il s'agit du nombre de "laisses":

  • +[alloc] initialise retainCount à 1.
  • -[autorelease] décrémente retainCount et place l'objet dans l'autorelease pool courant.
  • -[release] décrémente retainCount
  • -[retain] incrémente retainCount

Lorsque survient la fin de la boucle d'événements, l'autorelease pool libère tous les objets dont le retainCount est nul.

Constructeurs de commodité

Il arrive fréquemment de créer un objet, de le passer à un autre objet, puis de ne plus en avoir besoin. Les deux exemples suivants sont équivalents. Nous voulons passer une liste d'invités à un objet fiesta:

Exemple 1:

NSArray* invites = [[NSArray alloc] initWithObjects:@"Pascal", @"Florence", @"Martin", @"Patrick"];
[invites autorelease];
[fiesta setInvites:invites];

Exemple 2:

NSArray* invites = [NSArray arrayWithObjects:@"Pascal", @"Florence", @"Martin", @"Patrick"];
[fiesta setInvites:invites];

La méthode +[arrayWithObjects:] est un constructeur de commodité, qui permet de faire l'allocation, l'initialisation et l'autorelease en une seule opération. Ce type de méthodes est très courant dans Cocoa. En fait par convention, si le premier mot du nom d'une méthode est celui de la classe, vous est certain que l'objet renvoyé est autoreleasé.

Ajout à une collection

Pour finir, sachez que les collections retiennent les objets qu'elle contiennent. C'est à dire que les objets de types: NSArray, NSSet, NSDictionary et compagnie, envoient un message -[retain] aux objets qui leurs sont ajoutés. Quand la collection est libérée, elle envoie un message -[release] à tous les objets qu'elle contient.

Exemple:

@interface Livre : NSObject
{
    NSMutableArray* pages;  
}

@end

@implementation Livre
- (id) init
{
    if(self = [super init])
    {
        // Créer la liste des pages
        pages = [[NSMutableArray* alloc] init];

        // Ajouter une première page vierge
        Page* pageVierge = [[Page alloc] init];     // retainCount = 1
        [pages addObject:pageVierge];               // retainCount = 2
        [pageVierge release];                       // retainCount = 1
    }
    return self;    
}

- (void) dealloc
{
    [pages release];

    [super dealloc];    
}

L'objet pageVierge reçoit un message -[retain] quand elle est ajoutée au NSMutableArray pages. Nous devons donc lui envoyer un message -[release] pour qu'elle soit effectivement désallouée lorsque le livre sera désalloué.

Bientôt la suite

Nous n'en n'avons pas encore fini. Je vous encourage à poser des questions si un aspect n'est pas clair et que vous souhaitez le voir développé.

Renaud Pradenc
Céroce

La gestion de la mémoire (1)

03 11 2009 In: Cocoa, Objective-C

La gestion de la mémoire est un sujet qui — bien qu'aussi vieux que Cocoa elle-même — semble toujours aussi mal compris. Cette série d'articles va tenter d'expliquer les choses calmement pour que les nouveaux venus à Cocoa cessent de se torturer avec une question finalement assez simple quand on a bien assimilé deux ou trois principes de base.

Manuel ou automatique

Apple a profité de la version 10.5 de son système d'exploitation pour ajouter au langage Objective-C un ramasse-miettes (garbage collector), système bien connu des programmeurs en Java. Certains en sont partisans, d'autres les exècrent; personnellement, je pense qu'historiquement les langages de programmation évoluent pour éloigner le programmeur des arcanes de la machine, et que c'est une bonne chose: ce n'est pas quand on met au point un algorithme compliqué qu'on veut que la gestion de la mémoire nous pose problème.

Ceci dit, dans mes programmes ObjC, je gère toujours la mémoire manuellement parce que le ramasse-miettes est un rajout, et que Cocoa n'a pas été pensée pour fonctionner avec un ramasse-miettes; certaines choses en deviennent très complexes. Par ailleurs, avec le temps, de nombreux outils ont été développés pour aider à diagnostiquer les problèmes de gestion manuelle de la mémoire, rendant le débogage finalement assez facile.

Si vous visez des systèmes inférieurs à 10.5, ou plus probablement, que vous programmez l'iPhone, alors vous n'avez pas le choix, vous devrez gérer manuellement la mémoire. Continuez la lecture de cet article…

Principe n°1: L'objet qui alloue la mémoire est responsable de sa libération

Un objet alloue habituellement la mémoire pour un autre objet en envoyant le message -[alloc] à sa classe.

NSString* chaine = [NSString alloc];

Ce même objet devra libérer l'objet quand il n'en aura plus besoin, en lui envoyant un message -[release]:

[chaine release];

Allocation et libération d'une variable d'instance

Utilisons ici un exemple d'un programme possédant des classes Livre et Sommaire. Un Livre possède une seule instance de Sommaire:

@interface Livre: NSObject
{
     Sommaire*  sommaire;
}

@end

La méthode -init de Livre va ressembler à ceci:

- (id) init
{
    if (self = [super init])
    {
        sommaire = [[Sommaire alloc] init];
    }

    return self;
}

L'instance de Livre a alloué à sa création une instance de Sommaire. Selon le principe n°1, elle est donc responsable de sa libération. En général, elle le fera quand elle-même est sur le point de disparaître de la mémoire, dans sa méthode -[dealloc]:

- (void) dealloc
{
    [sommaire release];

    [super dealloc];    // Permettre à la classe parente de désallouer
                    // ses propres variables d'instance
}

Quelques compléments

copy

Un objet qui envoie un message -[copy] à un autre objet, alloue nécessairement la mémoire pour stocker la copie. Il devra donc libérer la copie, mais pas l'original.

new

Cette méthode équivaut à -[[alloc init]. Il faudra donc libérer la mémoire.

[self release]

Ne doit jamais être fait. C'est l'objet qui nous a alloué qui doit nous libérer.

Pour finir

Voilà déjà pour cette première partie qui couvrait un point essentiel. Nous verrons la prochaine fois comment passer un objet dont nous n'avons plus besoin à un autre objet, tout en étant sûr qu'il sera bien libéré.

Renaud Pradenc
Céroce

Le développement, plus que du code

27 10 2009 In: Cocoa, Entreprenariat, Interview, Liens

Pour changer, je vous propose aujourd'hui un billet qui ne parlera pas de code :

  • The Failure of the GPL est une réflexion sur la licence GPL et ses échecs. Le document dans son ensemble est très intéressant, mais la première page l'est tout particulièrement pour les développeurs Mac, car elle aborde le choix de LLVM par Apple et son abandon progressif de GCC.
  • From the Mouths of Developers est une interview très intéressante de divers auteurs de logiciels qui ont utilisés MacHeist afin de promouvoir leurs applications.
  • iTunes and Cocoa, une reflexion très intéressante de John Gruber sur iTunes et le peu d'intérêt actuel qu'aurait Apple à réécrire son logiciel en Cocoa.

LLVM : Quoi de nouveau ?

26 10 2009 In: C, Mac OS X, Objective-C

LLVM, le compilateur qu'Apple utilise de plus en plus (pour proposer les blocks, pour avoir un temps de compilation plus réduit) vient de sortir en version 2.6. Je vous invite si le sujet vous intéresse à lire les ressources suivantes :

Et si vous voulez suivre quelques conférences sur LLVM, il sera organisé un LLVM Camp à Paris le vendredi 20 novembre.

iPhone Tech Talk World Tour 2009

21 10 2009 In: Conférences

Si vous êtes inscrit en tant que développeur iPhone chez Apple, et que vous pouvez être sur Paris le lundi 9 novembre 2009, je vous invite à vous inscrire à la conférence iPhone Tech Talk World Tour 2009. D'autres dates sont disponibles un peu partout dans le monde, mais les places semblent partir très vite, je vous invite donc à vous inscrire au plus vite. Et pour ne rien gâcher, cette conférence est gratuite (ce qui explique très certainement la vitesse à laquelle parte les places disponible).

L'actualité iPhone de ces dernières semaines est actuellement assez calme, et il y a donc aujourd'hui assez peu de liens :

Une des grandes nouveautés de Snow Leopard, est l'arrivé de Grand Central Dispatch qui permet la mise en place des taches concurrentes sur plusieurs coeurs ou plusieurs processeurs. Mais la programmation concurrente implique un paradigme différents de ce que l'on a l'habitude de voir avec les langages objets ou impératifs.

Si vous voulez apprendre à utiliser Grand Central Dispatch (GCD), je vous invite à découvrir les ressources suivante pour une prise en main rapide :

Et si vous voulez aller plus loin, l'Apple Developer Connection propose les ressources suivantes :

Sachez en plus que Apple mis le code de GDC sous licence Open source sous le nom de libdispatch, et l'on peut noter qu'un port est d'ores et déjà disponible pour FreeBSD.

De retour de vacances, je vais commencer par m'intéresser aux dernières publications (livres, magazines, etc...) sur le développement à la fois Mac et iPhone :

L'actualité iPhone

14 09 2009 In: En vrac, iPhone / iPod Touch

Aujourd'hui, un billet 100% iPhone avant quelques jours sans publications (je serais en vacances loin de toute connexion Internet) :

Et de manière plus générale :

Snow Leopard : Quoi de nouveau ?

11 09 2009 In: Mac OS X

Snow Leopard, est maintenant sorti depuis un peu plus de deux semaines et l'on commence à avoir quelques retours de la part des développeurs sur les nouveautés :

  • Un point sur Open CL et Grand Central, chez MacBidouille vous permettra de comprendre l'intérêt de OpenCL/GrandCentral par le biais d'une interview de Christophe Ducommun le développeur de MovieGate.
  • Hidden Developer Gems in Snow Leopard revient sur toutes les nouvelles fonctionnalités de Snow Leopard dont Apple n'a pas forcement parlé sur son site, les nouveaux outils, le nettoyage de certaines API.
  • Snow Leopard excite les programmeurs aborde les nouveautés de Snow Leopard du point de vue d'un développeur. Il soulève à la fois les points positifs et négatifs.
  • Mac OS X 10.6 Source, vous permet de télécharger le code source d'une grande partie des outils qui compose Snow Leopard (le code de XNU le kernel de Mac OS X n'est quant à lui pas encore disponible).