Pergunta

//creates memory leak
  self.editMyObject = [[MyObject alloc] init];

//does not create memory leak
  MyObject *temp = [[MyObject alloc] init];
  self.editMyObject = temp;
  [temp release];

A primeira linha de código cria um vazamento de memória, mesmo se você fizer [liberação self.editMyObject] no método dealloc da classe. self.editMyObject é do tipo MeuObjeto. A segunda linha não incorre em perda de memória. não é a primeira linha apenas incorreta ou é uma maneira de liberar a memória?

Foi útil?

Solução

O comportamento correto depende da declaração do @property editMyObject. Assumindo que é declaradas como

@property (retain) id editMyObject; //id may be replaced by a more specific type

ou

@property (copy) id editMyObject;

então atribuição via self.editMyObject = mantém ou copia o objeto atribuído. Desde [[MyObject alloc] init] retorna um objeto retido, que você, como o chamador própria, você tem um extra de reter da instância MyObject e vai, portanto, vazar a menos que há uma liberação correspondente (como no segundo bloco). Eu sugiro que você leia a programação Gerenciamento de memória guia [2].

O segundo bloco de código está correto, assumindo a propriedade é declarada como descrito acima.

P.S. Você não deve usar [self.editMyObject release] em um método -dealloc. Você deve chamar [editMyObject release] (assumindo que o apoio ivar a @property é chamado editMyObject). Chamando o acessor (via self.editMyObject é seguro para acessores @synthesized, mas se um assessor anulado se baseia em estado de objeto (que pode não ser válida no local chamando -dealloc ou causar outros efeitos colaterais, você tem um bug chamando o assessor.

[2] regras de propriedade objeto em Cocoa são muito simples: se você chamar um método que tem alloc, ou copy em sua assinatura (ou +[NSObject new] uso que é basicamente equivalente a [[NSObject alloc] init]), então você "próprio" do objeto que é retornado e você deve equilibrar sua aquisição da propriedade com um release. Em todos os outros casos, você não possuir o objeto retornado de um método. Se você quiser mantê-lo, você deve tomar posse com um retain, e depois liberar a propriedade com um release.

Outras dicas

A sua propriedade é declarada "reter" o que significa que o passado nos objeto é automaticamente mantida.

Porque seu objeto já tinha uma contagem de referência de um dos alloc / init há então duas referências e eu estou supondo que apenas um release (em sua destructor).

Basicamente a chamada para self.editMyObject está realmente fazendo isso;

-(void) setEditMyObject:(MyObject*)obj
{
  if (editMyObject)
  {
    [editMyObject release];
    editMyObject = nil;
  }

  editMyObject = [obj retain];
}

Por convenção em Cocoa e Cocoa-touch, qualquer objeto criado usando [[SomeClass alloc] initX] ou [SomeClass newX] é criado com um manter a contagem de um. Você é responsável por chamar [someClassInstance release] quando você está feito com a sua nova instância, normalmente em seu método dealloc.

Onde isso fica complicado é quando você atribui o seu novo objeto para uma propriedade em vez de uma variável de instância. A maioria das propriedades são definidos como retain ou copy, o que significa que quer incrementar o objeto de manter a contagem quando definido, ou fazer uma cópia do objeto, deixando o original intocado.

No seu exemplo, você provavelmente tem isso em seu arquivo .h:

@property (retain) MyObject *editMyObject;

Assim, no seu primeiro exemplo:

// (2) property setter increments retain count to 2
self.editMyObject = 

    // (1) new object created with retain count of 1
    [[MyObject alloc] init];

// oops! retain count is now 2

Quando você cria sua nova instância MyObject usando alloc / init, ele tem uma contagem de um reter. Quando você atribui a nova instância para self.editMyObject, na verdade você está chamando o método -setEditMyObject: que o compilador cria para você quando você @synthesize editMyObject. Quando o compilador vê self.editMyObject = x, ele substitui-lo com [self setEditMyObject: x].

Em seu segundo exemplo:

MyObject *temp = [[MyObject alloc] init];
// (1) new object created with retain count of 1

self.editMyObject = temp;
// (2) equivalent to [self setEditMyObject: temp];
// increments retain count to 2

[temp release];
// (3) decrements retain count to 1

você segurar o seu novo objeto tempo suficiente para liberá-lo, de modo a manter a contagem é equilibrada (supondo que você liberá-lo em seu método dealloc).

Veja também estratégia de cacau para gerenciamento de ponteiro / memória

A primeira versão cria um objeto sem um lançamento correspondente. Quando você alloc o objeto, isso significa que você é proprietário do objeto. Seu setter presumivelmente mantém o objeto (como deveria), o que significa que agora possuem o objeto duas vezes. Você precisa da liberação para equilibrar a criação do objeto.

Você deve ler o Cacau gerenciamento de memória guia se você planeja usar cacau em tudo. Não é difícil uma vez que você aprender isso, mas é algo que você tem de aprender ou você vai ter um monte de problemas como este.

Todo mundo já cobriu por isso que faz com que um vazamento de memória, por isso vou apenas carrilhão com a forma de evitar a variável 'temp' e ainda evitar que um vazamento de memória:

self.editMyObject = [[[MyObject alloc] init] autorelease];

Isso vai deixar o seu (manter) propriedade como o único proprietário do novo objeto. Exatamente o mesmo resultado que o segundo exemplo, mas sem o objeto temporário.

Foi acordado e explicou que o código abaixo não tem um vazamento (Assumindo @property reter e @synthesize para editMyObject):

//does not create memory leak
MyObject *temp = [[MyObject alloc] init];
self.editMyObject = tempt;
[temp release];

Pergunta: nada de errado com o seguinte código que não usa um ponteiro temporário

?
//does not create memory leak ?
self.editMyObject = [[MyObject alloc] init];
[editMyObject release];

Para mim, isso parece OK.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top