2008-10-01 17 views
56

Quelle est la différence entre les deux méthodes suivantes pour allouer et init un objet?Objet allocate et init dans l'objectif C

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

et

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

La plupart de l'exemple de pomme utiliser la première méthode. Pourquoi alloueriez-vous, init et object, puis relâcheriez-vous immédiatement?

Répondre

70

Chaque objet a un nombre de références. Quand 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 1.
  2. L'objet est remis à self méthode setAController:, qui lui envoie un message retain (parce que la méthode ne sait pas d'où l'objet vient), incrémentant son nombre de référence à 2.
  3. Le code d'appel n'a plus besoin de l'objet lui-même, il appelle release, décrémenter le nombre de références à 1.

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

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

Il est important de se rappeler que self.property = foo; en Objective-C est vraiment juste un raccourci pour [self setProperty:foo]; et que la méthode setProperty: va être conserver ou copier des objets au besoin.

Si la propriété a été déclarée @property (copy), l'objet aurait été copié au lieu de conservé. Dans le premier exemple, l'objet original serait libéré immédiatement; dans le deuxième exemple, le nombre de références de l'objet original serait 1 même s'il devrait être 0. Donc vous voudriez 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 types de propriétés sont rares, généralement utilisés uniquement pour les délégués d'objet.

+0

C'est le cas que si 'aController' est déclarée avec le @property (conserver), non? –

+0

C'est exact. –

+0

C'est également le modèle correct si la propriété est déclarée (copie). Le deuxième modèle est correct pour une propriété déclarée (assigner) (ou avec la récupération de place activée). –

5

Notez également que votre désir de couper le code à une seule ligne est la raison pour laquelle beaucoup de gens utilisent Autorelease:

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

Bien que, en théorie, sur le autorelease iPhone est en quelque sorte plus cher (jamais entendu une explication claire pourquoi) et ainsi vous voudrez peut-être explicitement publier juste après avoir assigné l'objet ailleurs.

+0

Je ne pense pas que c'est plus cher que autorelease dans Cocoa. Juste que autorelease est plus cher que la libération. Donc, si vous pouvez libérer l'objet au lieu d'autoreleasing, vous devriez le faire. – schwa

+0

autorelease est plus cher car il doit trouver une structure de données locales et s'y ajouter (le pool autorelease), tandis que release libère juste un entier (le nombre de références). – benzado

+0

@benzado: Oui, la question était pourquoi autorelease plus cher ** sur l'iPhone **, pas pourquoi il est plus cher que la libération. –

4

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

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

+0

Il ne sert à rien de spécifier readwrite, puisqu'il s'agit de la valeur par défaut. – mk12

30

Comme d'autres l'ont noté, les deux extraits de code que vous montrez ne sont pas équivalents (pour des raisons de gestion de la mémoire). Quant à savoir pourquoi l'ancien est choisi sur celui-ci:

La formulation correcte de ce dernier serait

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

Par rapport à l'ancienne, ce qui ajoute une charge supplémentaire par l'utilisation de la piscine autorelease, et dans certains les circonstances conduiront à prolonger inutilement la durée de vie de l'objet (jusqu'à ce que le pool de libération automatique soit libéré), ce qui augmentera l'empreinte mémoire de votre application.

L'autre mise en œuvre « possible » (selon l'endroit où l'exemple est de) est simplement:

aController = [[AController alloc] init]; 

Cependant, définir une variable d'instance est fortement déconseillée directement ailleurs que dans une méthode init ou dealloc. Ailleurs, vous devriez toujours utiliser des méthodes d'accès.

Cela nous amène à la mise en œuvre montre exemple de code:

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

Cela suit les meilleures pratiques depuis:

  • Il évite autorelease;
  • Cela rend la sémantique de gestion de la mémoire immédiatement effacée;
  • Il utilise une méthode d'accès pour définir la variable d'instance.
+10

mmalc a raison. Mais ... en pratique, 3 lignes de code à assigner à 1 variable sont simplement folles. La plupart des équipes que j'ai vues utilisent l'approche autorelease "fais tout dans une seule ligne de code" - les frais généraux sont VANISHINGLY. Si vous avez déjà rencontré une situation sur iPhone où la différence de performance est importante, vous devriez probablement réécrire le code en C de toute façon - vous faites trop d'allocations. Peu d'équipes s'en tiendront à la convention «officielle» (à juste titre, à mon humble avis - les programmes du monde réel devraient être clairs, mais pas au point de la diarrhée verbale). – Adam

2

Vous pouvez également faire

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

avec une propriété de retenue, et il fonctionnerait de la même manière, mais son mieux d'utiliser l'autre sens (pour propriétés de rétention), car il est moins confus, ce code Cela donne l'impression que vous affectez un Contrôleur, puis il est effacé de la mémoire, alors que ce n'est pas le cas parce que setAController le conserve.

+0

Ce serait encore pire si c'était une propriété de copie. Vous libèreriez la copie, laissant un objet mort dans la variable d'instance, et fuyant l'objet que vous avez allocké et initié. –

+0

Mais ça marche si ça reste. Pas que tu devrais faire ça. – mk12

+0

Je pense vraiment que c'est une bonne approche si vous savez que la propriété est une propriété de conservation. Cela supprime un peu d'encombrement de code. – andrewz

5

Si vous utilisez Xcode, il peut vous aider à détecter ce code avec l'analyseur statique. Il suffit de frapper Construire >> Construire et Analyze

alt text

Cela vous montrera un message très utile à ces morceaux de code.

alt text