2010-02-12 5 views
5

Récemment, quelqu'un sur Stack Overflow m'a dit que le code ci-dessous ne fuit pas, que la propriété elle-même rétention poignées:iPhone: Est-ce une fuite ou non

self.locationManager = [[CLLocationManager alloc] init]; 

dans dealloc:

self.locationManager = nil; 

où dans le fichier .h:

@property (nonatomic, retain) CLLocationManager *locationManager; 

Je pensais que c'était une fuite évidente et croyait que cela devrait réparer la fuite:Cependant, il a prétendu que cela ne fonctionnerait pas, car selon ses propres termes: "vous n'autorelez pas les propriétés d'une classe." L'accesseur autogenerated d'une propriété définie pour conserver manipulera rétention automatiquement »

Et il m'a fait me demande s'il a tort ou ai-je pas compris à toute la gestion de la mémoire

EDIT 1: Le code est

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"]; 

différent que

self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 

? sage gestion de la mémoire

Le gars dit que le premier est correct et refuse le second. Pourquoi le second serait-il si mauvais? Autant que je puisse voir, les deux assignent des instances autoreleased à certaines propriétés, mais d'une manière ou d'une autre il y a toujours un argument têtu que le second est faux. Je ne peux pas le voir, toute aide serait la bienvenue.

+1

Sur votre EDIT 1, retenez-compte, ils sont identiques. Ces objets sont tous deux conservés une seule fois. Il y a une libération automatique implicite dans TOUTES les fonctions de commodité. Si vous ne voyez pas le mot alloc dans l'instruction init, la variable est autoeleased. Si vous utilisez alloc, vous devez utiliser autorelease –

Répondre

7

Le comptage conserve et libère des aides dans ce cas. C'est définitivement une fuite. Votre objet locationManager sera retenu 2 fois: une fois par les appels alloc/init, et une fois par la propriété. La définition de la propriété sur nil ne libère qu'une seule fois le locationManager.

Pour les exemples donnés dans Edit 1, ils sont en effet les mêmes. Il semble que l'autre développeur a une aversion pour l'autoreleasing immédiat ou ne comprend pas tout ce que fait autorelease.

0

Ajoutez cette ligne sous ... Elle indique évidemment le nombre de retenues pour locationManager. Cela vous indiquera si vous devez le libérer manuellement ou non.

NSLog(@"retainCount:%d", [locationManager retainCount]); 

modifier:

[locationManager release]; 
+0

Non, non, je demande conceptuellement. En outre, NSlogging retainCounts n'est pas une bonne mesure des fuites. –

+0

Oh ok. Bien pratique, vous devriez le faire dans dealloc parce que votre objet ne sera pas libéré lorsque le pool autorelease est drainé. Edité ci-dessus. – Zinc

+0

De la référence de classe NSObject: "Important: Cette méthode [retainCount] n'a généralement aucune valeur pour le débogage des problèmes de gestion de la mémoire [...] il est très peu probable que vous puissiez obtenir des informations utiles de cette méthode." – jnic

1

@jnic: vous êtes incroyablement mauvais. autant que je le comprends, la valeur précédente de la propriété est envoyée le message de libération, pas l'objet que vous souhaitez affecter à la propriété. donc, oui le code proposé fuit en effet et vous devez envoyer un message autorelease comme vous le pensiez, ahmet emrah

4

La sémantique du conservent option la propriété sont:

  • l'ancienne valeur (le cas échéant) reçoit un message de libération
  • la nouvelle valeur obtient un retain

Par conséquent, votre instance CLLocationManager aura un nombre de retenue de 2 après le setter. Un de attribue et un du dispositif de retenue. Vous devez envoyer un libération un message juste après le poseur:

CLLocationMamnager *aLocationManager = [[CLLocationManager alloc] init]; 
self.locationManager = aLocationManager; 
[aLocationManager release]; 

Vous pouvez également l'ajouter à la piscine autorelease de sorte qu'il sera au moins free'd éventuellement. Comme vous avez écrit:

self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 

Mieux encore, ne pas utiliser le option conserver la propriété. Laissez-le comme affectez (valeur par défaut) et vous êtes prêt à partir puisque vous utilisez de toute façon un objet conservé.

+0

Merci beaucoup pour la réponse –

-2

Bon, voici l'affaire.

Lorsque vous définissez une propriété comme si ...

@property (nonAtomic, retain) NSString myName; 

... en raison des défauts de la propriété Command est en fait comme le définir comme:

@property (nonAtomic, readwrite, retain, getter=getMyName,setter=setMyName) NSString myName; 

Lorsque vous utilisez @synthesize myName; Dans les coulisses, le compilateur génère une méthode getter qui ressemble à ceci:

-(void) setMyName:(NSString *) aString{ 
    if (!(myString==aString) { //test if a string is the same **object** as the current myString 
     if (aString != nil) { // if nil we don't want to send a retain 
      [aString retain]; // increment the retain count by one 
     }   
     [myString release]; //decrement the retain count of the currently assigned string by one. 
     myString=nil; //set the pointer to nil to ensure we don't point to the old string object 
     myString=aString; //assign the newly retained object to the myString symbol  
    } 
} 

Comme vous pouvez le voir, toute chaîne de n'importe quelle source, de tout compte de retenue préalable, auto-libérée ou non, sera automatiquement retenue par la méthode lors de l'assignation et lorsqu'une nouvelle valeur est assignée, elle sera automatiquement libérée par la méthode. Les affectations multiples n'empilent pas le nombre de retenues. Tant que vous utilisez le setter généré, l'objet affecté (dans ce cas aString) aura toujours un nombre de retain qui le maintiendra en vie dans la classe.

C'est pourquoi vous pouvez le faire ...

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"];

sans avoir à faire:

self.myName=[[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"] retain]; 

... et ne pas avoir à vous inquiéter si la valeur de chaîne disparaît soudainement lorsque les drains autoreleasepool.

Toutefois, si vous appelez jamais ...

[self.myName release]; 

... partout en dehors de la dealloc, l'objet dans la propriété de mon être nilled à moins que vous suivre implacablement. De même, si vous appelez ..

[self.myName retain]; 

... partout, l'objet dans la propriété fuira (peut-être même après que l'objet auto a été désallouée.)

C'est pourquoi je ne dis jamais de conserver ou autorelease tout objet ou affecté copié dans une propriété. Ce n'est pas seulement inutile mais contre-productif. Il s'ensuit que vous ne voulez appeler la libération d'une propriété que lorsque vous en avez terminé avec l'objet self car le suivi efficace du nombre de retenues par le setter signifie que vous pouvez ignorer la propriété même si vous en avez toujours besoin.

La libération automatique n'est jamais nécessaire pour une propriété car la propriété est toujours conservée par l'objet self et tout autre objet doit gérer la rétention interne s'il utilise la propriété de l'objet self. Une fois que vous avez compris ce qui se passe à l'intérieur des accesseurs générés, les règles sont évidentes. Ne conservez jamais l'objet d'une propriété explicitement. Ne jamais libérer l'objet d'une propriété, sauf en dealloc. Ne libérez jamais automatiquement l'objet d'une propriété.

Le corollaire évident de ces règles est toujours d'utiliser les références self.propertyName en interne dans l'objet self pour garantir que la rétention des propriétés est automatiquement gérée.

+0

Dude, self.myName = [NSSting stringWithFormat: @"% @ is correct. ", @" TechZen "]; est exactement le même que self.locationManager = [[[CLLocationManager alloc] init] autorelease]; gestion de la mémoire sage Je ne peux pas croire comment vous ne pouvez pas le voir. –

2

L'instruction suivante est retenue deux fois, et en tant que telle doit être libéré deux fois:

self.locationManager = [[CLLocationManager alloc] init]; 

Ceci est probablement mieux écrit:

self.locationManager = [[[CLLocationManager alloc] init]autorelease]; 

Maintenant, cette variable a été retenu une seule fois , et vous pouvez le libérer dans la fonction dealloc de votre classe.

En outre, si vous exécutez la ligne de code suivante, une version est appelée:

locationManager = nil; 

Depuis LocationManager est synthétisé, lorsque vous définissez à zéro, il est libéré en premier.

De plus, si vous avez fait ce qui suit, LocationManager serait d'abord libéré, puis remettre dans les coulisses:

self.locationManager = foo; 

Enfin, ce qui suit échouerait avec un EXC_BAD_ACCESS, parce que vous êtes à double libération LocationManager lorsque vous définissez à foo:

self.locationManager = [[[CLLocationManager alloc] init]autorelease]; 
[locationManager release]; 
self.locationManager = foo; 
1

règle simple: Si une propriété est marqué comme « conserver », donne toujours une variable autoreleased (si vous le créez) à moins bien sûr vous avez aussi besoin de le retenir ailleurs.