Como funciona o pool de liberação automática NSAutoreleasePool?

95

Pelo que entendi, qualquer coisa criada com uma alocação , nova ou cópia precisa ser liberada manualmente. Por exemplo:

int main(void) {
    NSString *string;
    string = [[NSString alloc] init];
    /* use the string */
    [string release];
}

Minha pergunta, porém, é que isso não seria tão válido ?:

int main(void) {
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
    [pool drain];
}
James Sumners
fonte

Respostas:

68

Sim, seu segundo trecho de código é perfeitamente válido.

Cada vez que -autorelease é enviado a um objeto, ele é adicionado ao pool de autorelease mais interno. Quando o pool é drenado, ele simplesmente envia -release para todos os objetos no pool.

Os pools de liberação automática são simplesmente uma conveniência que permite adiar o envio de liberação para "mais tarde". Esse "mais tarde" pode acontecer em vários lugares, mas o mais comum em aplicativos Cocoa GUI é no final do ciclo de loop de execução atual.

kperryua
fonte
5
onde está o fim do ciclo do loop de execução atual, se eu não tiver um loop?
Obrigado,
24
Não deveria "mais externo" ser "mais interno"?
Mike Weller
an objectdeveria ser an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.
1
EDIT: Alterado mais externo para mais interno.
chakrit
1
Importante: se você usar a contagem automática de referência (ARC), não poderá usar pools de liberação automática diretamente. Em vez disso, você usa blocos @autoreleasepool. De developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman
37

NSAutoreleasePool: drenagem vs. liberação

Visto que a função de draine releaseparece estar causando confusão, pode valer a pena esclarecer aqui (embora isso seja abordado na documentação ...).

Estritamente falando, da perspectiva do quadro geral nãodrain é equivalente a :release

Em um ambiente de contagem de referência, drainexecuta as mesmas operações que release, portanto, os dois são equivalentes nesse sentido. Para enfatizar, isso significa que você não vaza uma piscina se usar em drainvez de release.

Em um ambiente com coleta de lixo, releaseé um ambiente autônomo. Portanto, não tem efeito. drain, por outro lado, contém uma dica para o coletor de que ele deve "coletar se necessário". Portanto, em um ambiente com coleta de lixo, o uso drainajuda o sistema a equilibrar as varreduras de coleta.

mmalc
fonte
4
É fundamentalmente impossível 'vazar' a NSAutoreleasePool. Isso ocorre porque os pools funcionam como uma pilha. Instanciar um pool empurra aquele pool para o topo da pilha do pool de liberação automática de threads. -releasefaz com que aquele pool saia da pilha E quaisquer pools que foram colocados em cima dele, mas por alguma razão não foram retirados.
johne
7
De que forma isso é relevante para o que escrevi?
mmalc de
2
Eu gosto de como ele teve tempo para negrito E. SNAP!
Billy Gray
17

Como já apontado, seu segundo trecho de código está correto.

Eu gostaria de sugerir uma maneira mais sucinta de usar o pool de autorelease que funciona em todos os ambientes (contagem de ref, GC, ARC) e também evita a confusão de drenagem / liberação:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

No exemplo acima, observe o bloco @autoreleasepool . Isso está documentado aqui .

Neovibrante
fonte
2
Observe que a liberação automática não é permitida com o ARC.
dmirkitanov
1
Para esclarecer, é preciso usar o @autoreleasepoolbloco com ARC.
Simon
7

Não, você está errado. A documentação afirma claramente que sob não-GC, -drain é equivalente a -release, o que significa que o NSAutoreleasePool não vazará.

kperryua
fonte
Eu me perguntei por que o Xcode geraria código com -drain se fosse esse o caso. Usei -drain porque achei que era equivalente a -release com base no código gerado pelo Xcode.
James Sumners,
1
É fundamentalmente impossível 'vazar' um NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
johne
0

enviar liberação automática em vez de liberação para um objeto estende a vida útil desse objeto pelo menos até que o próprio pool seja drenado (pode ser mais longo se o objeto for retido posteriormente). Um objeto pode ser colocado no mesmo pool várias vezes e, nesse caso, ele recebe uma mensagem de liberação para cada vez que foi colocado no pool.

Hardik Mamtora
fonte
-2

Sim e não. Você acabaria liberando a memória da string, mas "vazando" o objeto NSAutoreleasePool na memória usando drenagem em vez de liberação se executasse isso em um ambiente de coleta de lixo (não gerenciado por memória). Esse "vazamento" simplesmente torna a instância de NSAutoreleasePool "inacessível" como qualquer outro objeto sem ponteiros fortes em GC, e o objeto seria limpo na próxima vez que GC fosse executado, o que poderia muito bem ser diretamente após a chamada para -drain:

drenar

Em um ambiente com coleta de lixo, aciona a coleta de lixo se a memória alocada desde a última coleta for maior que o limite atual; caso contrário, se comporta como liberação. ... Em um ambiente com coleta de lixo, esse método finalmente chama objc_collect_if_needed.

Caso contrário, é semelhante ao -releasecomportamento em não-GC, sim. Como outros afirmaram, -releaseé um ambiente autônomo no GC, então a única maneira de garantir que o pool funcione corretamente no GC é através -drain, e -drainno não-GC funciona exatamente como -releaseno não-GC, e pode-se dizer que comunica sua funcionalidade mais claramente conforme bem.

Devo ressaltar que sua instrução "qualquer coisa chamada com new, aloc ou init" não deve incluir "init" (mas deve incluir "copiar"), porque "init" não aloca memória, apenas configura o objeto (construtor moda). Se você recebeu um objeto alocado e sua função apenas chamada init como tal, você não o liberaria:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Isso não consome mais memória do que você já começou (assumindo que o init não instancia objetos, mas você não é responsável por eles).

Loren Segal
fonte
Não me sinto confortável em deixar esta resposta como aceita quando suas informações sobre drenagem não estiverem corretas. Consulte developer.apple.com/documentation/Cocoa/Reference/Foundation/… Atualize e irei aceitar novamente.
James Sumners,
O que há de incorreto na resposta? Em um ambiente com coleta de lixo (conforme declarado), o dreno não exclui o AutoReleasePool, portanto, você perderá memória, a menos que tenha usado a versão. A citação que listei veio direto da boca do cavalo, os documentos no ralo.
Loren Segal,
1
Loren: Em GC, - [NSAutoreleasePool drenar] irá desencadear uma coleção. -retain, -release e -autorelease são todos ignorados pelo coletor; é por isso que -drain é usado em pools de liberação automática sob GC.
Chris Hanson
Na documentação para 'drenagem': Em um ambiente de memória gerenciada, isso se comporta da mesma forma que chamar a liberação. Assim, você não perderá memória se usar 'drenar' em vez de liberar.
mmalc,
-[NSAutoreleasePool release]em um ambiente com coleta de lixo é autônomo. -[NSAutoreleasePool drain]funciona em ambientes com contagem de referência e com coleta de lixo.
Jonathan Sterling