3

在这样的关系中如何避免内存泄漏?

@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应该预先解除分配,您只关心您不再需要它,引用计数如何以及在何处降至零?

此外,Leaks 应用程序甚至会将其检测为泄漏吗?我怀疑我可能被这个咬了,因为我试图追查似乎是泄漏的地方,但 Leaks 声称我没有泄漏。由于 child 仍然引用 parent,反之亦然,我敢说 Leaks 认为对象仍然被引用,因此不会泄漏。

4

2 回答 2

5

根据经验,在父子关系中保留分层祖先是一个坏主意。它会导致这些“保留周期”,在这些“保留周期”中,您的对象不会在它们应该被释放时被释放。这里有很好的图片和建议,很好地解释了这个问题。

于 2010-11-14T06:30:00.810 回答
3

苹果关于保留周期的文档还指出了如何打破它们:在一侧使用弱引用,在这种情况下可能是针对父母的。

请注意,您发布的内容还有其他问题:考虑例如-setChild:when aNode==child. 该child实例将在-retainif nothing else 持有(强)引用之前被释放。

要解决该问题,请使用以下任一方法:

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

或者:

Node *tmp = child;
child = [aNode retain];
[tmp release];
于 2010-11-14T06:45:04.333 回答