Цель-C: круговые ссылки типа «ребенок-родитель», утечка? - PullRequest
3 голосов
/ 14 ноября 2010

Как избежать утечек памяти в таких отношениях?

@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 даже обнаружит это как утечку? Я подозреваю, что меня это укусило, поскольку я пытаюсь отследить то, что кажется утечкой, но Утечки утверждают, что у меня нет утечек. Поскольку child по-прежнему ссылается на parent, и наоборот, я осмелюсь сказать, что Leaks считает, что на объекты все еще ссылаются, и, следовательно, не происходит утечка.

Ответы [ 2 ]

5 голосов
/ 14 ноября 2010

Как правило, плохая идея - сохранять иерархического предка в родительских дочерних отношениях. Это вызывает эти «циклы сохранения», когда ваши объекты не будут освобождены, когда они должны. Здесь отличное объяснение проблемы здесь с красивыми картинками и советами.

3 голосов
/ 14 ноября 2010

Документация Apple по сохранению циклов также указывает, как их нарушать: используйте слабые ссылки с одной стороны, в этом случае, вероятно, для родителей.

Обратите внимание, что есть другие проблемы с тем, что вы опубликовали: Рассмотрим, например, -setChild: когда aNode==child. Экземпляр child будет освобожден до -retain, если ничто иное не имеет (сильной) ссылки на него.

Чтобы исправить это, используйте:

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

или

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