2010-11-14 5 views
3

Comment éviter les fuites de mémoire dans une relation comme celle-ci?Objectif-C: Références circulaires type enfant-parent, fuite?

@class Node; 

@interface Node : NSObject { 
    Node *parent; 
    Node *child; 
    id object; 
} 

-(id)initWithObject:(id)anObject; 
-(id)object; 
-(void)setChild:(Node *)aNode; 
-(void)setParent:(Node *)aNode; 

@end 


@implementation Node 

-(id)initWithObject:(id)anObject { 
    if (self = [self init]) { 
    object = [anObject retain]; 
    } 
    return self; 
} 

-(id)object { 
    return object; 
} 

-(void)setParent:(Node *)aNode { 
    [parent release]; 
    parent = [aNode retain]; 
} 

-(void)setChild:(Node *)aNode { 
    [child release]; 
    child = [aNode retain]; 
    [child setParent:self]; 
} 

-(void)dealloc { 
    [child release]; 
    [parent release]; 
    [super dealloc]; 
} 

@end 

Node *root = [[Node alloc] initWithObject:@"foo"]; // root retain = 1 
Node *child = [[Node alloc] initWithObject:@"bar"]; // child retain = 1 

[root setChild:child]; // child retain = 2, root retain = 2 

[root release]; // root retain = 1 
[child release]; // child retain = 1 

/* Leaking! */ 

Si vous ne savez pas à l'avance root devriez dealloc'd, vous vous souciez seulement que vous ne avez plus besoin, comment et d'où la baisse de comptage de référence à zéro?

De plus, l'application Leaks détecterait-elle même cela comme une fuite? Je soupçonne que j'ai peut-être été mordu par ce que j'essaie de traquer ce qui semble être une fuite, mais les fuites prétend que je n'ai pas de fuite. Puisque l'enfant fait toujours référence au parent, et inversement, j'ose dire que Leaks considère que les objets sont toujours référencés, et donc ne fuit pas.

Répondre

5

En règle générale, c'est une mauvaise idée de conserver un ancêtre hiérarchique dans une relation parent-enfant. Il provoque ces "cycles de retenue" où vos objets ne seront pas libérés quand ils sont supposés le faire. Il y a une excellente explication du problème here avec de belles photos et des conseils.

+1

Parfait, merci. En bref, utilisez l'affectation dans '-setParent:' et dans la méthode '-dealloc' envoyez' setParent: nil' à l'enfant. Cela a un sens parfait. – d11wtq

+0

Désolé, une dernière chose. Ai-je raison de penser que ce type de fuite de mémoire serait invisible pour un outil de détection de fuites? – d11wtq

+0

Je ne suis pas sûr de cela, ça fait un moment que j'ai utilisé des fuites :( – GWW

3

La documentation de pommes sur retain cycles10 vous indique également comment les casser: utilisez weak references d'un côté, dans ce cas probablement pour les parents.

Notez qu'il y a d'autres problèmes avec ce que vous avez posté: Considérons par ex. lorsque aNode==child. L'instance child sera désallouée avant le -retain si rien d'autre ne contient une référence (forte).

Pour que l'utilisation soit fixer:

if (aNode != child) { 
    // ... same as before 
} 

ou:

Node *tmp = child; 
child = [aNode retain]; 
[tmp release]; 
+0

Yikes, merci, j'aurai répété le même code dans d'autres endroits alors je vais devoir réparer ça! – d11wtq

Questions connexes