Pergunta

Estou apenas começando a dar uma olhada no Objective-C e no Cocoa com o objetivo de brincar com o SDK do iPhone.Estou razoavelmente confortável com C's malloc e free conceito, mas o esquema de contagem de referências do Cocoa me deixou bastante confuso.Disseram-me que é muito elegante quando você entende, mas ainda não superei o problema.

Como release, retain e autorelease funcionam e quais são as convenções sobre seu uso?

(Ou, na falta disso, o que você leu que o ajudou a conseguir isso?)

Foi útil?

Solução

Vamos começar com retain e release; autorelease é realmente apenas um caso especial, uma vez que você entende os conceitos básicos.

No Cocoa, cada objeto registra quantas vezes está sendo referenciado (especificamente, o NSObject a classe base implementa isso).Ao ligar retain em um objeto, você está dizendo que deseja aumentar sua contagem de referências em um.Ao ligar release, você informa ao objeto que está abandonando-o e sua contagem de referências é diminuída.Se, depois de ligar release, a contagem de referência agora é zero, então a memória desse objeto é liberada pelo sistema.

A maneira básica como isso difere de malloc e free é que qualquer objeto não precisa se preocupar com a falha de outras partes do sistema porque você liberou a memória que elas estavam usando.Supondo que todos estejam acompanhando e retendo/liberando de acordo com as regras, quando um trecho de código retém e depois libera o objeto, qualquer outro trecho de código que também faça referência ao objeto não será afetado.

O que às vezes pode ser confuso é saber as circunstâncias sob as quais você deve ligar retain e release.Minha regra geral é que, se eu quiser manter um objeto por algum tempo (se for uma variável de membro de uma classe, por exemplo), preciso ter certeza de que a contagem de referências do objeto sabe sobre mim.Conforme descrito acima, a contagem de referência de um objeto é incrementada chamando retain.Por convenção, também é incrementado (definido como 1, na verdade) quando o objeto é criado com um método "init".Em qualquer um desses casos, é minha responsabilidade ligar release no objeto quando eu terminar com ele.Caso contrário, haverá um vazamento de memória.

Exemplo de criação de objeto:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Para agora autorelease.A liberação automática é usada como uma maneira conveniente (e às vezes necessária) de informar ao sistema para liberar esse objeto depois de um tempo.Do ponto de vista do encanamento, quando autorelease é chamado, o thread atual NSAutoreleasePool é alertado da chamada.O NSAutoreleasePool agora sabe que, uma vez obtida uma oportunidade (após a iteração atual do loop de eventos), ele pode chamar release no objeto.Da nossa perspectiva como programadores, é preciso chamar release para nós, então não precisamos (e na verdade, não deveríamos).

O que é importante notar é que (novamente, por convenção) toda criação de objetos aula métodos retornam um objeto liberado automaticamente.Por exemplo, no exemplo a seguir, a variável "s" tem uma contagem de referência de 1, mas após a conclusão do loop de eventos, ela será destruída.

NSString* s = [NSString stringWithString:@"Hello World"];

Se você quiser manter essa string, precisará ligar retain explicitamente e depois explicitamente release quando terminar.

Considere o seguinte trecho de código (muito inventado) e você verá uma situação em que autorelease é necessário:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

Sei que tudo isso é um pouco confuso - em algum momento, porém, vai dar um clique.Aqui estão algumas referências para você começar:

  • Introdução da Apple ao gerenciamento de memória.
  • Programação Cocoa para Mac OS X (4ª Edição), de Aaron Hillegas - um livro muito bem escrito com muitos exemplos excelentes.Parece um tutorial.
  • Se você estiver realmente mergulhando, você pode ir para Grande Rancho Nerd.Este é um centro de treinamento dirigido por Aaron Hillegas – o autor do livro mencionado acima.Participei do curso de Introdução ao Cacau lá há vários anos e foi uma ótima maneira de aprender.

Outras dicas

Se você entende o processo de retenção/liberação, então existem duas regras de ouro que são "duh" óbvias para os programadores Cocoa estabelecidos, mas infelizmente raramente são explicadas claramente para os recém-chegados.

  1. Se uma função que retorna um objeto tiver alloc, create ou copy em seu nome então o objeto é seu.Você deve ligar [object release] quando você terminar com isso.Ou CFRelease(object), se for um objeto Core-Foundation.

  2. Se NÃO tiver uma dessas palavras em seu nome, o objeto pertence a outra pessoa.Você deve ligar [object retain] se você deseja manter o objeto após o término da sua função.

Você faria bem em seguir esta convenção também nas funções que você mesmo cria.

(Calticeiros:Sim, infelizmente existem algumas chamadas de API que são exceções a essas regras, mas são raras).

Se você estiver escrevendo código para a área de trabalho e puder direcionar o Mac OS X 10.5, você deve pelo menos considerar o uso da coleta de lixo do Objective-C.Isso realmente simplificará a maior parte do seu desenvolvimento - é por isso que a Apple colocou todo o esforço para criá-lo e fazê-lo funcionar bem.

Quanto às regras de gerenciamento de memória quando não estiver usando GC:

  • Se você criar um novo objeto usando +alloc/+allocWithZone:, +new, -copy ou -mutableCopy ou se você -retain um objeto, você está assumindo a propriedade dele e deve garantir que ele seja enviado -release.
  • Se você receber um objeto de qualquer outra forma, você estará não o proprietário dele e deve não certifique-se de que foi enviado -release.
  • Se você quiser ter certeza de que um objeto será enviado -release você mesmo pode enviar isso ou pode enviar o objeto -autorelease e o atual pool de liberação automática vou enviá-lo -release (uma vez por recebido -autorelease) quando a piscina é drenada.

Tipicamente -autorelease é usado como uma forma de garantir que os objetos vivam durante o evento atual, mas sejam limpos posteriormente, pois há um pool de liberação automática que envolve o processamento de eventos do Cocoa.No cacau, é distante é mais comum retornar objetos liberados automaticamente para um chamador do que retornar objetos que o próprio chamador precisa liberar.

Usos do Objective-C Contagem de Referência, o que significa que cada objeto tem uma contagem de referência.Quando um objeto é criado, ele possui uma contagem de referência “1”.Simplificando, quando um objeto é referenciado (ou seja, armazenado em algum lugar), ele é "retido", o que significa que sua contagem de referências aumenta em um.Quando um objeto não é mais necessário, ele é "liberado", o que significa que sua contagem de referências diminui em um.

Quando a contagem de referência de um objeto é 0, o objeto é liberado.Esta é a contagem básica de referência.

Para alguns idiomas, as referências são aumentadas e diminuídas automaticamente, mas o objetivo-c não é um desses idiomas.Assim, o programador é responsável por reter e liberar.

Uma maneira típica de escrever um método é:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

O problema de precisar lembrar de liberar quaisquer recursos adquiridos dentro do código é tedioso e sujeito a erros.Objective-C introduz outro conceito que visa tornar isso muito mais fácil:Conjuntos de liberação automática.Pools de liberação automática são objetos especiais instalados em cada thread.Eles são uma classe bastante simples, se você procurar NSAutoreleasePool.

Quando um objeto recebe uma mensagem de "autorelease" enviada a ele, o objeto procurará por quaisquer pools de autorelease na pilha para este thread atual.Ele adicionará o objeto à lista como um objeto para o qual enviar uma mensagem de "liberação" em algum momento no futuro, que geralmente é quando o próprio pool é liberado.

Pegando o código acima, você pode reescrevê-lo para ser mais curto e fácil de ler, dizendo:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Como o objeto é liberado automaticamente, não precisamos mais chamar explicitamente "release" nele.Isso ocorre porque sabemos que algum pool de liberação automática fará isso por nós mais tarde.

Espero que isso ajude.O artigo da Wikipedia é muito bom sobre contagem de referências.Mais informações sobre pools de liberação automática podem ser encontrados aqui.Observe também que se você estiver compilando para Mac OS X 10.5 e posterior, poderá instruir o Xcode a compilar com a coleta de lixo habilitada, permitindo ignorar completamente a retenção/liberação/liberação automática.

Joshua (#6591) - O material de coleta de lixo no Mac OS X 10.5 parece muito legal, mas não está disponível para o iPhone (ou se você quiser que seu aplicativo seja executado em versões anteriores à 10.5 do Mac OS X).

Além disso, se você estiver escrevendo uma biblioteca ou algo que possa ser reutilizado, o uso do modo GC impede que qualquer pessoa que use o código também use o modo GC, pelo que entendi, qualquer pessoa que tente escrever código amplamente reutilizável tende a gerenciar memória manualmente.

Como sempre, quando as pessoas começam a tentar reformular o material de referência, quase invariavelmente erram algo ou fornecem uma descrição incompleta.

A Apple fornece uma descrição completa do sistema de gerenciamento de memória do Cocoa em Guia de programação de gerenciamento de memória para Cocoa, ao final do qual há um breve mas preciso resumo do Regras de gerenciamento de memória.

Não vou acrescentar nada específico sobre retenção/liberação, a não ser que você queira pensar em gastar US$ 50 e obter o livro de Hillegass, mas sugiro fortemente que você comece a usar as ferramentas de Instrumentos bem no início do desenvolvimento de seu aplicativo (até mesmo seu primeiro!).Para fazer isso, execute->Iniciar com ferramentas de desempenho.Eu começaria com Leaks, que é apenas um dos muitos instrumentos disponíveis, mas ajudará a mostrar quando você se esqueceu de lançar.Não é assustador a quantidade de informações que você receberá.Mas confira este tutorial para começar e ir rápido:
TUTORIAL DE CACAU:CORRIGINDO VAZAMENTOS DE MEMÓRIA COM INSTRUMENTOS

Na verdade tentando força vazamentos podem ser a melhor maneira de, por sua vez, aprender como evitá-los!Boa sorte ;)

Matt Dillard escreveu:

return [[s liberação automática] liberação];

A liberação automática faz não reter o objeto.O Autorelease simplesmente o coloca na fila para ser liberado mais tarde.Você não quer ter uma declaração de lançamento aí.

Minha coleção habitual de artigos sobre gerenciamento de memória Cocoa:

gerenciamento de memória de cacau

Há um screencast gratuito disponível na rede iDeveloperTV

Gerenciamento de memória em Objective-C

A resposta do NilObject é um bom começo.Aqui estão algumas informações complementares relativas ao gerenciamento manual de memória (obrigatório no iPhone).

Se você pessoalmente alloc/init um objeto, ele vem com uma contagem de referência de 1.Você é responsável por limpá-lo quando não for mais necessário, ligando para [foo release] ou [foo autorelease].release limpa-o imediatamente, enquanto autorelease adiciona o objeto ao pool de autorelease, que o liberará automaticamente posteriormente.

autorelease é principalmente para quando você tem um método que precisa retornar o objeto em questão (então você não pode liberá-lo manualmente, caso contrário você retornará um objeto nulo), mas você também não quer segurá-lo.

Se você adquirir um objeto onde não chamou alloc/init para obtê-lo - por exemplo:

foo = [NSString stringWithString:@"hello"];

mas você deseja manter esse objeto, você precisa chamar [foo reter].Caso contrário, é possível que isso aconteça autoreleased e você estará segurando uma referência nula (como seria no acima stringWithString exemplo).Quando não precisar mais, ligue [foo release].

As respostas acima fornecem declarações claras sobre o que diz a documentação;o problema que a maioria das pessoas novas enfrenta são os casos não documentados.Por exemplo:

  • Liberação automática:Os documentos dizem que ele desencadeará um lançamento "em algum momento no futuro". QUANDO?!Basicamente, você pode contar com a presença do objeto até sair do código e voltar ao loop de eventos do sistema.O sistema PODE liberar o objeto a qualquer momento após o ciclo de eventos atual.(Acho que Matt disse isso antes.)

  • Sequências estáticas: NSString *foo = @"bar"; - você tem que reter ou liberar isso?Não.Que tal

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • A regra de criação:Se você o criou, você o possui e espera-se que o libere.

Em geral, a maneira como os novos programadores do Cocoa ficam confusos é não entender quais rotinas retornam um objeto com um retainCount > 0.

Aqui está um trecho de Regras muito simples para gerenciamento de memória no Cocoa:

Regras de contagem de retenção

  • Dentro de um determinado bloco, o uso de -copy, -alloc e -retain deve ser igual ao uso de -release e -autorelease.
  • Objetos criados usando construtores de conveniência (por exemplostringWithString do NSString) são considerados liberados automaticamente.
  • Implemente um método -dealloc para liberar as variáveis ​​de instância que você possui

O primeiro marcador diz:se você ligou alloc (ou new fooCopy), você precisa chamar release nesse objeto.

O segundo marcador diz:se você usar um construtor de conveniência e você precisa do objeto para ficar por perto (como acontece com uma imagem a ser desenhada posteriormente), você precisa retê-la (e liberá-la posteriormente).

O terceiro deve ser autoexplicativo.

Muitas informações boas sobre o cocoadev também:

Como várias pessoas já mencionaram, o da Apple Introdução ao gerenciamento de memória é de longe o melhor lugar para começar.

Um link útil que ainda não vi mencionado é Gerenciamento prático de memória.Você o encontrará no meio dos documentos da Apple se os ler, mas vale a pena criar um link direto.É um resumo executivo brilhante das regras de gerenciamento de memória com exemplos e erros comuns (basicamente o que outras respostas aqui estão tentando explicar, mas não tão bem).

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