toronto - iphone xr screen size




Entendendo a contagem de referência com cacau e objetivo-C (10)

Matt Dillard escreveu :

return [[liberação de autorelease]]];

A liberação automática não mantém o objeto. Autorelease simplesmente coloca na fila para ser liberado mais tarde. Você não quer ter uma declaração de lançamento lá.

Estou apenas começando a dar uma olhada no Objective-C e no Cocoa com o objetivo de jogar com o iPhone SDK. Estou razoavelmente confortável com o conceito malloc e free C, mas o esquema de contagem de referências de Cocoa me deixa bastante confuso. Me disseram que é muito elegante quando você entende, mas eu ainda não acabei.

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

(Ou, na sua falta, o que você leu e ajudou você a entender?)


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

Se você alloc/init um objeto pessoalmente, ele vem com uma contagem de referência igual a 1. Você é responsável por limpá-lo quando não for mais necessário, seja chamando [foo release] ou [foo autorelease] . release limpa imediatamente, enquanto autorelease adiciona o objeto ao pool de autorelease, que será liberado automaticamente em um momento posterior.

Autorelease é principalmente para quando você tem um método que precisa retornar o objeto em questão ( assim você não pode liberá-lo manualmente, senão você estará retornando um objeto nulo ), mas você não quer segurá-lo, seja .

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

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

mas você quer se agarrar a este objeto, você precisa chamar [foo reter]. Caso contrário, é possível que ele seja stringWithString e você estará segurando uma referência nula (como no exemplo stringWithString acima ). Quando você não precisar mais, chame [foo release] .



Como várias pessoas já mencionaram, o developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… da Apple é de longe o melhor lugar para começar.

Um link útil que eu não vi mencionado ainda é o developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . Você vai encontrá-lo no meio dos documentos da Apple, se você lê-los, mas vale a pena ligar direto. É um resumo executivo brilhante das regras de gerenciamento de memória com exemplos e erros comuns (basicamente o que outras respostas estão tentando explicar, mas não tão bem).


Joshua (# 6591) - O material da coleção Garbage 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 nas versões anteriores a 10.5 do Mac OS X).

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



Não vou acrescentar nada ao específico de reter / liberar, a não ser que você queira pensar em gastar US $ 50 e obter o livro da Hillegass, mas sugiro fortemente começar a usar as ferramentas de instrumentos muito cedo no desenvolvimento de sua aplicação (até mesmo 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 liberar. É muito difícil saber com quanta informação você será apresentado. Mas confira este tutorial para se levantar e ir rápido:
TUTORIAL DE CACAU: FIXANDO FUGAS DE MEMÓRIA COM INSTRUMENTOS

Na verdade, tentar forçar vazamentos pode ser uma maneira melhor de, por sua vez, aprender como evitá-los! Boa sorte ;)


Objective-C usa Reference Counting , o que significa que cada Object tem uma contagem de referência. Quando um objeto é criado, ele tem uma contagem de referência de "1". Simplesmente falando, quando um objeto é referido (ou seja, armazenado em algum lugar), ele fica "retido", o que significa que sua contagem de referência é aumentada em um. Quando um objeto não é mais necessário, ele é "liberado", o que significa que sua contagem de referência é diminuída em um.

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

Para alguns idiomas, as referências são automaticamente aumentadas e diminuídas, mas o objective-c não é uma dessas linguagens. 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-se de liberar quaisquer recursos adquiridos dentro do código é tedioso e propenso a erros. Objective-C introduz outro conceito destinado a tornar isso muito mais fácil: Autorelease Pools. Os pools de liberação automática são objetos especiais instalados em cada encadeamento. Eles são uma classe bastante simples, se você procurar NSAutoreleasePool.

Quando um objeto recebe uma mensagem "autorelease" enviada para ele, o objeto procurará por qualquer pool de autorelease na pilha do thread atual. Ele adicionará o objeto à lista como um objeto para enviar uma mensagem "release" em algum momento no futuro, que geralmente é quando o próprio pool é liberado.

Tomando o código acima, você pode reescrevê-lo para ser mais curto e mais 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". Isso é porque sabemos que alguns grupos de autorelease farão isso por nós mais tarde.

Espero que isso ajude. O artigo da Wikipedia é muito bom sobre a contagem de referência. Mais informações sobre pools autorelease podem ser encontradas aqui . Observe também que, se você estiver criando para o Mac OS X 10.5 e posterior, poderá informar ao Xcode para construir com a coleta de lixo ativada, permitindo ignorar completamente a retenção / liberação / autorelease.


Se você estiver escrevendo código para a área de trabalho e puder segmentar o Mac OS X 10.5, deve pelo menos examinar a coleta de lixo do Objective-C. Ele realmente simplificará a maior parte do seu desenvolvimento - é por isso que a Apple se empenhou em criá-lo em primeiro lugar e fazer com que ele tenha um bom desempenho.

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

  • Se você criar um novo objeto usando +alloc/+allocWithZone: +new , -copy ou -mutableCopy ou se você -retain um objeto, você estará apropriando-se dele e deverá garantir que ele seja enviado -release .
  • Se você receber um objeto de qualquer outra forma, você não é o proprietário dele e não deve garantir que ele seja enviado.
  • Se você quiser garantir que um objeto seja enviado, -autorelease -o ou envie o objeto -autorelease e o pool de -autorelease atual o enviará -release (uma vez por recebimento -autorelease ) quando o pool for drenado.

Tipicamente, -autorelease é usada como uma maneira de assegurar que os objetos vivam durante o evento atual, mas são limpos depois, já que há um pool de autorelease que envolve o processamento de eventos do Cocoa. No Cocoa, é muito mais comum retornar objetos a um chamador que são liberados do que retornar objetos que o próprio chamador precisa liberar.


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

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

A maneira básica como isso difere do malloc e do free é que qualquer objeto não precisa se preocupar com outras partes do sistema, porque você liberou a memória que estava usando. Supondo que todos estejam jogando e retendo / liberando de acordo com as regras, quando uma parte do código retém e depois libera o objeto, qualquer outra parte do código que 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 para retain e release . Minha regra geral é que se eu quiser me agarrar a um objeto por algum tempo (se for uma variável de membro em uma classe, por exemplo), então eu preciso ter certeza de que a contagem de referência do objeto me conhece. Conforme descrito acima, a contagem de referência de um objeto é incrementada chamando retain . Por convenção, também é incrementado (definido como 1, realmente) quando o objeto é criado com um método "init". Em qualquer um desses casos, é minha responsabilidade chamar release no objeto quando eu terminar com ele. Se eu não fizer isso, 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

Agora para autorelease . Autorelease é usado como uma maneira conveniente (e às vezes necessária) de dizer ao sistema para liberar esse objeto depois de um tempo. Do ponto de vista do encanamento, quando o autorelease é chamado, o NSAutoreleasePool do encadeamento atual é alertado sobre a chamada. O NSAutoreleasePool agora sabe que, uma vez que obtém uma oportunidade (após a iteração atual do loop de eventos), ele pode chamar release no objeto. De nossa perspectiva como programadores, ele cuida de 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) todos os métodos de classe de criação de objeto retornam um objeto autoreleased. Por exemplo, no exemplo a seguir, a variável "s" tem uma contagem de referência de 1, mas depois que o loop de evento for concluído, ele será destruído.

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

Se você quiser se agarrar a essa string, precisará chamar retain explicitamente e depois release la explicitamente quando terminar.

Considere o seguinte código de código (muito artificial) e você verá uma situação em que o 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];
}

Eu percebo que tudo isso é um pouco confuso - em algum momento, porém, ele vai clicar. Aqui estão algumas referências para você ir:

  • Introdução da Apple ao gerenciamento de memória.
  • Cacau Programação para Mac OS X (4ª Edição) , por Aaron Hillegas - um livro muito bem escrito com muitos grandes exemplos. Parece um tutorial.
  • Se você está realmente mergulhando, você pode ir ao Big Nerd Ranch . Este é um centro de treinamento dirigido por Aaron Hillegas - o autor do livro mencionado acima. Eu participei do curso Intro to Cocoa há alguns anos atrás, e foi uma ótima maneira de aprender.




memory