La gestion de la mémoire (1)

03112009 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

Articles similaires