Gestion de la mémoire Obj-C

La gestion de la mémoire est l'un des processus les plus importants de tout langage de programmation. C'est le processus par lequel la mémoire des objets est allouée quand ils sont nécessaires et désallouée lorsqu'ils ne sont plus nécessaires.

La gestion de la mémoire objet est une question de performance; si une application ne libère pas d'objets inutiles, son encombrement mémoire augmente et les performances en souffrent.

Les techniques de gestion de la mémoire Objective-C peuvent être globalement classées en deux types.

  • "Manual Retain-Release" ou MRR
  • "Comptage de référence automatique" ou ARC

"Manual Retain-Release" ou MRR

Dans MRR, nous gérons explicitement la mémoire en gardant nous-mêmes la trace des objets. Cela est implémenté à l'aide d'un modèle, appelé comptage de références, que la classe Foundation NSObject fournit en conjonction avec l'environnement d'exécution.

La seule différence entre MRR et ARC est que la rétention et la libération sont gérées par nous manuellement dans le premier alors que sa prise en charge est automatique dans le second.

La figure suivante représente un exemple du fonctionnement de la gestion de la mémoire dans Objective-C.

Le cycle de vie de la mémoire de l'objet de classe A est illustré dans la figure ci-dessus. Comme vous pouvez le voir, le nombre de rétention est affiché sous l'objet, lorsque le nombre de rétention d'un objet devient 0, l'objet est complètement libéré et sa mémoire est désallouée pour d'autres objets à utiliser.

L'objet de classe A est d'abord créé à l'aide de la méthode alloc / init disponible dans NSObject. Maintenant, le nombre de rétention devient 1.

Désormais, la classe B conserve l'objet de la classe A et le nombre de rétention de l'objet de la classe A devient 2.

Ensuite, la classe C fait une copie de l'objet. Maintenant, il est créé comme une autre instance de la classe A avec les mêmes valeurs pour les variables d'instance. Ici, le nombre de rétention est 1 et non le nombre de rétention de l'objet d'origine. Ceci est représenté par la ligne pointillée sur la figure.

L'objet copié est libéré par la classe C à l'aide de la méthode de libération et le nombre de rétention devient 0 et, par conséquent, l'objet est détruit.

Dans le cas de l'objet de classe A initial, le compte de rétention est de 2 et il doit être libéré deux fois pour qu'il soit détruit. Ceci est fait par des instructions de libération de classe A et de classe B qui décrémentent le nombre de rétention à 1 et 0, respectivement. Enfin, l'objet est détruit.

Règles de base MRR

  • Nous possédons tout objet que nous créons: Nous créons un objet en utilisant une méthode dont le nom commence par "alloc", "new", "copy" ou "mutableCopy"

  • Nous pouvons prendre possession d'un objet à l'aide de retenir: un objet reçu est normalement garanti pour rester valide dans la méthode dans laquelle il a été reçu, et cette méthode peut également renvoyer l'objet en toute sécurité à son invocateur. Nous utilisons retenir dans deux situations -

    • Dans l'implémentation d'une méthode accesseur ou d'une méthode init, pour s'approprier un objet que nous voulons stocker en tant que valeur de propriété.

    • Pour éviter qu'un objet ne soit invalidé en tant qu'effet secondaire d'une autre opération.

  • Lorsque nous n'en avons plus besoin, nous devons renoncer à la propriété d'un objet que nous possédons: nous renonçons à la propriété d'un objet en lui envoyant un message de libération ou un message de libération automatique. Dans la terminologie Cocoa, renoncer à la propriété d'un objet est donc généralement appelé «libérer» un objet.

  • Vous ne devez pas renoncer à la propriété d'un objet que vous ne possédez pas: c'est juste le corollaire des règles de stratégie précédentes énoncées explicitement.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");
   
   // Should set the object to nil
   sampleClass = nil;
   return 0;
}

Lorsque nous compilerons le programme ci-dessus, nous obtiendrons la sortie suivante.

2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this

"Comptage de référence automatique" ou ARC

Dans le comptage automatique de références ou ARC, le système utilise le même système de comptage de références que MRR, mais il insère la méthode de gestion de la mémoire appropriée pour nous au moment de la compilation. Nous sommes fortement encouragés à utiliser ARC pour de nouveaux projets. Si nous utilisons ARC, il n'est généralement pas nécessaire de comprendre l'implémentation sous-jacente décrite dans ce document, même si cela peut être utile dans certaines situations. Pour plus d'informations sur ARC, consultez les notes de mise à jour de la transition vers ARC.

Comme mentionné ci-dessus, dans ARC, nous n'avons pas besoin d'ajouter des méthodes de libération et de conservation car cela sera pris en charge par le compilateur. En fait, le processus sous-jacent d'Objective-C est toujours le même. Il utilise les opérations de conservation et de libération en interne, ce qui facilite le codage pour le développeur sans se soucier de ces opérations, ce qui réduira à la fois la quantité de code écrit et la possibilité de fuites de mémoire.

Il y avait un autre principe appelé garbage collection, qui est utilisé dans Mac OS-X avec MRR, mais depuis sa dépréciation dans OS-X Mountain Lion, il n'a pas été discuté avec MRR. De plus, les objets iOS n'ont jamais eu de fonction de garbage collection. Et avec ARC, le garbage collection n'est pas non plus utilisé dans OS-X.

Voici un exemple d'ARC simple. Notez que cela ne fonctionnera pas sur le compilateur en ligne car il ne prend pas en charge ARC.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

Lorsque nous compilerons le programme ci-dessus, nous obtiendrons la sortie suivante.

2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated