2010-05-22 8 views
2

J'ai un problème de fuite de mémoire qui ne peut tout simplement pas comprendre! Suivre cette méthode d'initialisation:La fuite de mémoire pour NSDictionary chargé par fichier plist

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera { 

if (self = [super init]) { 

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist]; 
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary: 
           [[[NSDictionary dictionaryWithContentsOfFile:pathOpere] 
           objectForKey:nomeCompositore] 
           objectForKey:nomeOpera]]; 

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera]; 
    self.compositore = [[NSString alloc] initWithString:nomeCompositore]; 
    self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]]; 
} 

return self;} 

Alors cette petite variation (note self.tipologia):

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera { 

if (self = [super init]) { 

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist]; 
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary: 
           [[[NSDictionary dictionaryWithContentsOfFile:pathOpere] 
           objectForKey:nomeCompositore] 
           objectForKey:nomeOpera]]; 

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera]; 
    self.compositore = [[NSString alloc] initWithString:nomeCompositore]; 
    self.tipologia = [[NSString alloc] initWithString:@"Test"]; 
} 

return self;} 

Dans la première variante est généré une fuite de mémoire, le second est pas! Et je ne peux pas comprendre pourquoi! La fuite de mémoire est mise en évidence par des instruments, mis en évidence la ligne:

[NSDictionary dictionaryWithContentsOfFile:pathOpere] 

Ceci est la méthode dealloc:

- (void)dealloc { 
[tipologia release]; 
[compositore release]; 
[nomeCompleto release]; 
[super dealloc];} 

Répondre

1

Rappelez-vous que alloc retourne un objet que vous possédez. Si vous avez déclaré vos trois propriétés de chaîne comme retain, l'affectation de ces objets à vos propriétés signifie que vous possédez désormais chacune d'elles deux fois: une fois parce que vous l'avez verrouillé, et encore parce que vous l'avez affectée à votre propriété. Les objets restent en vie car rien ne libère leurs seconds propriétaires.

Si vous avez déclaré les propriétés copy (ce qui est la méthode correcte pour déclarer une propriété NSString), l'affectation de l'objet stocke une copie en tant que valeur de la propriété. Vous ne faites rien d'autre avec les objets originaux, qui restent vivants car rien ne les libère.

De toute façon, c'est votre fuite.

La propriété doit être déclarée copy; si c'est déjà le cas, n'essayez pas de réparer la fuite en changeant cela.

Vous ne devez pas utiliser l'accès aux propriétés ici. N'oubliez pas que l'affectation à une propriété est un message set<PropertyName>: et que votre objet n'est pas encore entièrement initialisé. Envoyer un message à un objet incomplètement initialisé ou incomplètement libéré demande des problèmes, en particulier lorsque des sous-classes sont impliquées, car elles peuvent surcharger les méthodes d'accès d'une manière inattendue pour la superclasse.

Donc, dans init seulement, affecter directement aux variables d'instance.Dans dealloc uniquement, envoyez release des messages directement aux objets dans les variables d'instance. Partout ailleurs, utilisez les accès aux propriétés. Vous ne devriez pas non plus utiliser alloc et initWithString: ici. Cela fonctionnera, mais la convention est d'envoyer des messages copy aux objets que vous avez déjà, de la même manière que les propriétés le feraient. Envoyez des messages copy à vos objets de chaîne de saisie, puis affectez les copies à vos variables d'instance. Lorsque vous utilisez des accès aux propriétés, utilisez les constructors convenience (stringWith…:, par exemple), car ils renvoient des objets qui ne vous appartiennent pas. Lorsque vous affectez ces objets à vos propriétés copy -déclarées, vous stockerez en réalité des copies que vous possédez.

L'autre façon serait d'utiliser alloc et initWithWhatever:, puis immédiatement autorelease que cet objet avant de l'assigner à la propriété; De cette façon, crée un objet que vous possédez, puis abandonne immédiatement la propriété avant de l'attribuer à la propriété.

+0

Merci Peter Hosey, maintenant je comprends le fonctionnement du mécanisme. Merci beaucoup! – Pask

0

Essayez

nomeCompleto = [[NSString alloc] initWithString:nomeOpera]; 
compositore = [[NSString alloc] initWithString:nomeCompositore]; 
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]]; 

ou

self.nomeCompleto = nomeOpera; 
self.compositore = nomeCompositore; 
self.tipologia = [dicOpera objectForKey:kKeyTipologia]; 

au lieu de self.xxx = [[yyy alloc] init...].


Dans le code d'origine, l'ERS de l'affectation retourne un objet de conserver le nombre 1, et si vous faites le @property ayant (retain) ou (copy), le nombre retenir final serait +2. Par conséquent, même si vous les publiez dans -dealloc, le nombre net de retenues est +1, provoquant une fuite de mémoire.


BTW, il est inutile d'appeler +dictionaryWithDictionary:. Il suffit d'utiliser

NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere] 
          objectForKey:nomeCompositore] 
          objectForKey:nomeOpera]; 
+0

Tout fonctionne, merci KennyTM! – Pask