Na maior parte do tempo com o ARC (contagem automática de referência), não precisamos pensar em gerenciamento de memória com objetos Objective-C. Não é mais permitido criar NSAutoreleasePool
s, no entanto, há uma nova sintaxe:
@autoreleasepool {
…
}
Minha pergunta é: por que eu precisaria disso quando não deveria liberar / liberar manualmente manualmente?
EDIT: Para resumir o que eu tirei de todas as respostas e comentários de forma sucinta:
Nova sintaxe:
@autoreleasepool { … }
é uma nova sintaxe para
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
Mais importante:
- O ARC usa
autorelease
e tambémrelease
. - Ele precisa de um pool de liberação automática para fazer isso.
- O ARC não cria o pool de liberação automática para você. Contudo:
- O segmento principal de todo aplicativo Cocoa já possui um pool de liberação automática.
- Há duas ocasiões em que você pode querer usar
@autoreleasepool
:- Quando você está em um encadeamento secundário e não há um pool de liberação automática, deve fazer o seu próprio para evitar vazamentos, como
myRunLoop(…) { @autoreleasepool { … } return success; }
. - Quando você deseja criar um pool mais local, como @mattjgalloway mostrou em sua resposta.
- Quando você está em um encadeamento secundário e não há um pool de liberação automática, deve fazer o seu próprio para evitar vazamentos, como
Respostas:
O ARC não se livra de retenções, lançamentos e autoreleases, apenas adiciona os necessários para você. Portanto, ainda há chamadas a serem retidas, ainda há chamadas a liberar, ainda há chamadas a liberação automática e ainda existem pools de liberação automática.
Uma das outras alterações que eles fizeram com o novo compilador Clang 3.0 e o ARC é que eles foram substituídos
NSAutoReleasePool
pela@autoreleasepool
diretiva do compilador.NSAutoReleasePool
sempre foi um "objeto" especial de qualquer maneira e eles o criaram para que a sintaxe do uso de um não seja confundida com um objeto, de modo que geralmente é um pouco mais simples.Então, basicamente, você precisa,
@autoreleasepool
porque ainda existem pools de liberação automática para se preocupar. Você não precisa se preocupar em adicionarautorelease
chamadas.Um exemplo de uso de um pool de liberação automática:
Um exemplo bastante artificial, com certeza, mas se você não tivesse o
@autoreleasepool
interior dofor
loop externo, liberaria 100000000 objetos posteriormente, em vez de 10000 a cada vez que ofor
loop externo fosse lançado .Atualização: veja também esta resposta - https://stackoverflow.com/a/7950636/1068248 - por que
@autoreleasepool
nada tem a ver com o ARC.Atualizar: dei uma olhada nas informações internas do que está acontecendo aqui e escrevi no meu blog . Se você der uma olhada lá, verá exatamente o que o ARC está fazendo e como o novo estilo
@autoreleasepool
e como ele introduz um escopo é usado pelo compilador para inferir informações sobre o que retém, liberações e autoreleases são necessários.fonte
@autoreleasepool
para mim também? Se não estiver controlando o que é liberado ou liberado automaticamente (o ARC faz isso por mim), como devo saber quando configurar um pool de autorelease?@autoreleasepool
não libera automaticamente nada. Ele cria um pool de liberação automática, para que quando o final do bloco for atingido, quaisquer objetos que foram liberados automaticamente pelo ARC enquanto o bloco estava ativo receberão mensagens de liberação. O Guia de programação avançada de gerenciamento de memória da Apple explica assim:fonte
release
mensagem, mas se a contagem de retenção for> 1, o objeto NÃO será desalocado.As pessoas geralmente entendem mal o ARC para algum tipo de coleta de lixo ou algo semelhante. A verdade é que, depois de algum tempo, as pessoas da Apple (graças aos projetos llvm e clang) perceberam que a administração de memória do Objective-C (todos os
retains
ereleases
etc.) pode ser totalmente automatizada em tempo de compilação . Isto é, apenas lendo o código, mesmo antes de ser executado! :)Para fazer isso, existe apenas uma condição: DEVEMOS seguir as regras , caso contrário, o compilador não poderá automatizar o processo no momento da compilação. Assim, para garantir que nós não quebrar as regras, não é permitido explicitamente escrita
release
,retain
etc. Essas chamadas são injetados automaticamente em nosso código pelo compilador. Por isso internamente ainda temosautorelease
s,retain
,release
, etc. É apenas que não precisamos de escrevê-los mais.O A do ARC é automático no tempo de compilação, o que é muito melhor do que no tempo de execução, como a coleta de lixo.
Ainda o temos,
@autoreleasepool{...}
porque o fato de não infringir nenhuma das regras, somos livres para criar / drenar nossa piscina sempre que precisarmos :).fonte
É porque você ainda precisa fornecer ao compilador dicas sobre quando é seguro que objetos liberados automaticamente saiam do escopo.
fonte
@autoreleasepool
porque não sei se o ARC pode decidir liberar algo automaticamente?Citado em https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html :
...
fonte
Os pools de liberação automática são necessários para retornar objetos recém-criados de um método. Por exemplo, considere este pedaço de código:
A sequência criada no método terá uma contagem de retenção de um. Agora, quem equilibrará os que mantêm a conta com uma liberação?
O método em si? Não é possível, ele precisa retornar o objeto criado, portanto, não deve liberá-lo antes de retornar.
O chamador do método? O chamador não espera recuperar um objeto que precise ser liberado, o nome do método não implica que um novo objeto seja criado, apenas diz que um objeto é retornado e esse objeto retornado pode ser um novo que requer liberação, mas pode bem ser um existente que não. O retorno do método pode até depender de algum estado interno; portanto, o chamador não pode saber se precisa liberar esse objeto e não deve se preocupar.
Se o chamador sempre liberasse todos os objetos retornados por convenção, todos os objetos não criados recentemente sempre teriam que ser retidos antes de retorná-los de um método e teriam que ser liberados pelo chamador assim que sair do escopo, a menos que é retornado novamente. Isso seria altamente ineficiente em muitos casos, pois é possível evitar completamente alterar as contagens de retenção em muitos casos, se o chamador nem sempre liberar o objeto retornado.
É por isso que existem pools de autorelease, para que o primeiro método se torne realmente
A chamada
autorelease
de um objeto o adiciona ao pool de liberação automática, mas o que isso realmente significa, adicionando um objeto ao pool de liberação automática? Bem, significa dizer ao seu sistema " Eu quero que você libere esse objeto para mim, mas em algum momento posterior, não agora; ele tem uma contagem de retenção que precisa ser equilibrada por um release, caso contrário a memória vazará, mas eu não posso fazer isso sozinho No momento, como preciso que o objeto permaneça vivo além do meu escopo atual e meu interlocutor também não fará isso por mim, ele não tem conhecimento de que isso precisa ser feito.Então, adicione-o ao seu pool e depois de limpar o piscina, também limpe meu objeto para mim. "Com o ARC, o compilador decide quando reter um objeto, quando liberar um objeto e quando adicioná-lo a um pool de liberação automática, mas ainda exige a presença de pools de liberação automática para poder retornar objetos recém-criados de métodos sem perda de memória. A Apple acaba de fazer algumas otimizações bacanas no código gerado, que às vezes eliminam os pools de liberação automática durante o tempo de execução. Essas otimizações exigem que ambos, o chamador e o destinatário estejam usando o ARC (lembre-se de misturar ARC e não-ARC é legal e também oficialmente suportado) e, se esse for realmente o caso, só poderá ser conhecido em tempo de execução.
Considere este código ARC:
O código que o sistema gera, pode se comportar como o código a seguir (que é a versão segura que permite combinar livremente código ARC e não-ARC):
(Observe que a retenção / liberação no chamador é apenas uma retenção de segurança defensiva, não é estritamente necessária, o código estaria perfeitamente correto sem ele)
Ou pode se comportar como este código, caso ambos sejam detectados para usar o ARC em tempo de execução:
Como você pode ver, a Apple elimina o lançamento ativo, assim também a liberação atrasada do objeto quando a piscina é destruída, bem como a retenção de segurança. Para saber mais sobre como isso é possível e o que realmente está acontecendo nos bastidores, confira esta postagem no blog.
Agora, para a pergunta real: por que alguém usaria
@autoreleasepool
?Para a maioria dos desenvolvedores, resta apenas um motivo hoje para usar essa construção em seu código: manter a área de memória pequena, quando aplicável. Por exemplo, considere este loop:
Suponha que toda chamada para
tempObjectForData
possa criar um novoTempObject
que seja retornado automaticamente. O loop for criará um milhão desses objetos temporários que são todos coletados no pool de liberação automática atual e somente quando esse pool for destruído, todos os objetos temporários também serão destruídos. Até que isso aconteça, você tem um milhão desses objetos temporários na memória.Se você escrever o código assim:
Em seguida, um novo pool é criado toda vez que o loop for é executado e é destruído no final de cada iteração do loop. Dessa forma, no máximo, um objeto temporário permanece na memória a qualquer momento, apesar do loop ser executado um milhão de vezes.
No passado, você também costumava gerenciar os auto-releasespools ao gerenciar threads (por exemplo, usando
NSThread
), pois apenas o thread principal possui automaticamente um pool de auto-release para um aplicativo Cocoa / UIKit. No entanto, isso é praticamente um legado hoje, pois hoje você provavelmente não usaria threads para começar. Você usaria GCDsDispatchQueue
ouNSOperationQueue
's' e esses dois gerenciam um pool de liberação automática de nível superior para você, criado antes da execução de um bloco / tarefa e destruído uma vez concluído.fonte
Parece haver muita confusão sobre esse tópico (e pelo menos 80 pessoas que provavelmente agora estão confusas sobre isso e acham que precisam espalhar o @autoreleasepool em torno de seu código).
Se um projeto (incluindo suas dependências) usar exclusivamente o ARC, o @autoreleasepool nunca precisará ser usado e não fará nada útil. O ARC manipulará a liberação de objetos no momento correto. Por exemplo:
exibe:
Cada objeto de teste é desalocado assim que o valor fica fora do escopo, sem aguardar a saída de um conjunto de liberação automática. (O mesmo ocorre com o exemplo NSNumber; isso apenas nos permite observar o desdobramento.) O ARC não usa o autorelease.
A razão pela qual @autoreleasepool ainda é permitida é para projetos ARC e não-ARC mistos, que ainda não fizeram a transição completa para o ARC.
Se você chamar um código não-ARC, ele poderá retornar um objeto liberado automaticamente. Nesse caso, o loop acima vazaria, pois o pool de liberação automática atual nunca será encerrado. É aí que você deseja colocar um @autoreleasepool em torno do bloco de código.
Mas se você fez completamente a transição do ARC, esqueça o pool de liberação automática.
fonte
+ (Testing *) testing { return [Testing new] }
. Então você verá que o acordo não será chamado até mais tarde. Isso é corrigido se você envolver o interior do loop em um@autoreleasepool
bloco.+ (Testing *) testing { return [Testing new];} + (void) test { while(true) NSLog(@"p = %p", [self testing]);}
[UIImage imageWithData]
a equação, de repente, comecei a ver oautorelease
comportamento tradicional , exigindo@autoreleasepool
manter o pico de memória em algum nível razoável.