web-dev-qa-db-fra.com

Attribuer et initier un objet dans l'objectif C

Quelle est la différence entre les 2 façons suivantes d'allouer et d'initier un objet?

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

et

self.aController= [[AController alloc] init];

La plupart des exemples Apple Apple utilisent la première méthode. Pourquoi allouer, initier et objecter puis relâcher immédiatement?

56
Ronnie Liew

Chaque objet a un compte de référence. Lorsqu'il passe à 0, l'objet est désalloué.

En supposant que la propriété a été déclarée comme @property (retain):

Votre premier exemple, ligne par ligne:

  1. L'objet est créé par alloc, il a un compte de référence de 1.
  2. L'objet est remis à la méthode setAController: De self, qui lui envoie un message retain (car la méthode ne sait pas d'où vient l'objet), incrémentant sa référence compter jusqu'à 2.
  3. Le code appelant n'a plus besoin de l'objet lui-même, il appelle donc release, décrémentant le nombre de références à 1.

Votre deuxième exemple fait essentiellement les étapes 1 et 2 mais pas 3, donc à la fin le nombre de références de l'objet est 2.

La règle est que si vous créez un objet, vous êtes responsable de le libérer lorsque vous en avez terminé. Dans votre exemple, le code est effectué avec tempAController après avoir défini la propriété. Il est de la responsabilité de la méthode setter d'appeler retain si elle a besoin que cet objet reste.

Il est important de se rappeler que self.property = foo; Dans Objective-C est vraiment juste un raccourci pour [self setProperty:foo]; Et que la méthode setProperty: Va conserver ou copier des objets selon les besoins.

Si la propriété avait été déclarée @property (copy), alors l'objet aurait été copié au lieu d'être conservé. Dans le premier exemple, l'objet d'origine serait libéré immédiatement; dans le deuxième exemple, le nombre de références de l'objet d'origine serait 1 même s'il devrait être 0. Vous voudrez donc toujours écrire votre code de la même manière.

Si la propriété a été déclarée @property (assign), alors self ne revendique pas la propriété de l'objet, et quelqu'un d'autre doit le conserver. Dans ce cas, le premier exemple serait incorrect. Ces sortes de propriétés sont rares, généralement utilisées uniquement pour les délégués d'objets.

70
benzado

Comme d'autres l'ont noté, les deux extraits de code que vous affichez ne sont pas équivalents (pour des raisons de gestion de la mémoire). Quant à savoir pourquoi le premier est préféré au second:

La formulation correcte de ce dernier serait

self.aController= [[[AController alloc] init] autorelease];

Par rapport au premier, cela ajoute une surcharge supplémentaire grâce à l'utilisation du pool de libération automatique, et dans certaines circonstances, la durée de vie de l'objet sera inutilement prolongée (jusqu'à ce que le pool de libération automatique soit libéré), ce qui augmentera l'empreinte mémoire de votre application.

L'autre implémentation "possible" (selon la provenance de l'exemple) est simplement:

aController = [[AController alloc] init];

Cependant, la définition d'une variable d'instance directement est fortement déconseillée ailleurs que dans une méthode init ou dealloc. Ailleurs, vous devez toujours utiliser des méthodes d'accesseur.

Cela nous amène alors à l'implémentation montrée dans l'exemple de code:

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

Cela suit les meilleures pratiques depuis:

  • Il évite la libération automatique;
  • Cela rend la sémantique de gestion de la mémoire immédiatement claire;
  • Il utilise une méthode d'accesseur pour définir la variable d'instance.
30
mmalc

Si vous utilisez Xcode, il peut vous aider à détecter ce code avec l'analyseur statique. Appuyez simplement sur Build >> Build and Analyze

alt text

Cela vous montrera un message très utile sur de tels morceaux de code.

alt text

5
leviathan

Notez également que votre désir de réduire le code à une seule ligne est la raison pour laquelle de nombreuses personnes utilisent Autorelease:

self.aController = [[[AController alloc] init] autorelease];

Bien qu'en théorie, la libération automatique de l'iPhone soit en quelque sorte plus coûteuse (vous n'avez jamais entendu d'explication claire pourquoi) et vous voudrez donc libérer explicitement juste après avoir assigné l'objet ailleurs.

Une autre chose à noter est que votre exemple dépend également de la définition @property d'un aController.

Si elle était définie comme @property (readwrite, retain) id aController; alors votre exemple fonctionne, alors que si elle est définie comme @property (readwrite, assign) id aController; alors l'appel supplémentaire à libérer entraînerait la désallocation de votre objet.

4
Ashley Clark

Vous pourriez aussi faire

@property (nonatomic, retain)AController *aController;
...
self.aController= [[AController alloc] init];
[aController release];

avec une propriété de rétention, et cela fonctionnerait de la même manière, mais il vaut mieux utiliser l'autre manière (pour conserver les propriétés) car c'est moins déroutant, ce code donne l'impression que vous affectez un contrôleur et ensuite il est supprimé de la mémoire, alors qu'en réalité ce n'est pas le cas car setAController le conserve.

2
mk12