【问题标题】:Objective-C: Child-parent type circular references, leaking?Objective-C:子父类型循环引用,泄漏?
【发布时间】:2011-05-09 18:03:00
【问题描述】:

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

@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 认为对象仍然被引用,因此没有泄漏。

【问题讨论】:

    标签: objective-c circular-reference


    【解决方案1】:

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

    【讨论】:

    • 完美,谢谢。简而言之,在-setParent: 中使用赋值,并在-dealloc 方法中将setParent:nil 发送给孩子。很有道理。
    • 抱歉,还有一件事。我是否认为这种类型的内存泄漏对于泄漏检测工具来说是不可见的?
    • 我不确定,我已经有一段时间没有使用泄漏了:(
    • 不,leaks 和/或 Instruments 应该能够发现这种泄漏,只要整个层次结构无法从堆栈或静态/全局内存中访问。
    • 有趣。它似乎没有发现这一点。我只知道它与循环引用有关。我打开活动监视器,从我的应用程序中记录内存使用情况,打开一个文档,进行一些更改,关闭文档,内存增加了。重复。内存再次上升,等等。我刚刚删除了泄漏源。 NSView 和 NSViewController 之间的循环引用。当我打开和关闭文档时,内存现在按预期上升和下降。泄漏没有看到这一点。
    【解决方案2】:

    retain cycles 上的 Apples 文档还指出了如何破解它们:在一侧使用 weak references,在这种情况下可能是为了父母。

    请注意,您发布的内容还有其他问题: 考虑例如-setChild:aNode==childchild 实例将在 -retain 之前被释放,如果没有其他东西持有对它的(强)引用。

    要解决这个问题,请使用:

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

    或:

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

    【讨论】:

    • 哎呀,谢谢,我会在其他地方重复类似的代码,所以我必须修复它!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 2011-01-01
    • 2011-06-07
    • 1970-01-01
    相关资源
    最近更新 更多