2010-11-14 61 views
3

如何避免像這樣的關係中的內存泄漏?Objective-C:子類父類型的循環引用泄漏?

@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! */ 

如果您不知道的前期root應dealloc'd,你只在乎你不再需要它,如何以及在何處並引用計數下降到零?

此外,泄漏應用程序甚至會檢測到這是泄漏?我懷疑自己可能被這件事咬了一口,因爲我試圖追查似乎是泄漏的東西,但Leaks聲稱我沒有泄漏。由於孩子仍然引用父母,反之亦然,我敢說泄漏認爲對象仍然被引用,因此不泄漏。

回答

5

作爲一個經驗法則,在親子關係中保留一個層級祖先是一個壞主意。它會導致這些「保留週期」,當你的對象被設想時不會被釋放。有一個很好的解釋問題here與漂亮的圖片和建議。

+1

完美,謝謝。簡而言之,在'-setParent:'和'-dealloc'方法中使用賦值將'setParent:nil'發送給孩子。非常有意義。 – d11wtq 2010-11-14 06:37:34

+0

對不起,還有一件事。我是否認爲這種類型的內存泄漏對於泄漏檢測工具是不可見的? – d11wtq 2010-11-14 06:38:26

+0

我並不確定,因爲我已經使用了泄漏:( – GWW 2010-11-14 06:39:44

3

retain cycles的蘋果文檔也指出你如何打破它們:一方面使用weak references,在這種情況下可能是父母。

請注意,您發佈的內容還存在其他問題: 考慮例如-setChild:當時aNode==child。如果沒有其他內容保存對(強)的引用,則child實例將在-retain之前解除分配。

爲了解決這個問題兼用:

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

或:

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

Yikes,謝謝,我會在其他地方重複類似的代碼,所以我必須解決這個問題! – d11wtq 2010-11-14 06:56:37