Por que a autorelease é especialmente perigosa / cara para aplicativos do iPhone?

Eu estou procurando uma fonte primária (ou uma boa explicação) para sustentar a alegação de que o uso de autorelease é perigoso ou excessivamente caro ao escrever software para o iPhone.

Vários desenvolvedores fazem essa afirmação, e eu até ouvi dizer que a Apple não a recomenda, mas eu não consegui encontrar nenhuma fonte concreta para fazer o backup.

Referências SO:
autorelease-iphone
Por que isso cria um memory leaks (iPhone)?

Nota: Eu posso ver, do ponto de vista conceitual, que autorelease é um pouco mais caro do que uma simples chamada para release , mas eu não acho que uma pequena penalidade seja suficiente para fazer a Apple recomendar contra ela.

Qual é a história real?

(não pode aceitar sua própria resposta?)

Bem, depois de tudo isso, consegui encontrar uma referência do Apple Developer, adicionada como uma nota lateral na parte inferior da página:

iPhone OS Nota: Como no iPhone OS um aplicativo é executado em um ambiente com restrição de memory, o uso de pools autorelease é desencorajado em methods ou blocos de código (por exemplo, loops) em que um aplicativo cria muitos objects. Em vez disso, você deve liberar explicitamente objects sempre que possível.

Ainda assim, isso sugere usar autorelease com cuidado, não evitando-o completamente.

(e agora para o meu comentário)

Parece que há uma certa sobrecarga na manutenção da piscina. Eu li este artigo que me levaria a evitar autorelease, tanto quanto possível, porque eu prefiro que as coisas sejam consistentes. Se você tem alguma memory sob autorelease e outra memory sendo totalmente gerenciada manualmente, isso pode ser um pouco mais confuso.

Não é a questão de usar ou não usar autorelease, porque, em alguns casos, o autorelease é a única maneira pela qual você passará. A questão deve ser ” Por que não usar autorelease em todos os objects, em vez de usar reter e liberar? “.

Para responder a isso, você deve primeiro aprender o que é um uso adequado para autorelease. Digamos que você tenha uma class que tenha duas propriedades: firstName e lastName. Há um getter e um setter para cada um. Mas você também precisa de um método que retorne fullName, concatenando essas duas strings em uma nova string:

 - (NSString *) fullName { NSString str = [[NSString alloc]initWithFormat:@"%@ %@", firstName, lastName]; // this is not good until we put [str autorelease]; return str; } 

O que há de errado com essa foto? A contagem de referência na string retornada é 1, portanto, se você não quiser vazar, o chamador deve liberá-la quando terminar. Do ponto de vista do chamador, ele apenas solicitou um valor de propriedade fullName . Ele não está ciente do fato de que ele recebeu um novo object que deveria liberar após o uso, e não alguma referência a um NSString mantido internamente pela class!

Se colocarmos a [str release] antes de retornar, a string seria destruída e o método retornaria lixo! É aí que usamos [str autorelease] , para marcar o object para ser liberado mais tarde (geralmente quando o processamento do evento é feito). Dessa forma, o chamador recebe seu object e não precisa se preocupar se deve liberá-lo ou não.

A convenção é chamar autorelease em um novo object antes que o método retorne ao chamador. Exceções são methods com nomes que começam com alloc , new ou copy . Nesses casos, os chamadores sabem que um novo object é criado para eles e é seu dever chamar a liberação desse object.

Substituir a release com autorelease é uma má ideia, já que os objects se acumulam e entopem a memory muito rapidamente, especialmente em loops. Os resources do iPhone são limitados, por isso, para minimizar a sobrecarga de memory, é seu dever liberar o object assim que estiver pronto.

Eu discordo que evitar completamente a autorelease é sábio.

O Cocoa Touch o utiliza com bastante frequência internamente e, em muitas situações, é a única maneira de alocar memory adequadamente (um bom exemplo são as células de exibição de tabela reutilizáveis). Se você entender o que está acontecendo, o pool de autorelease é uma ótima ferramenta à sua disposição. A principal coisa a lembrar é que os blocos não são liberados até algum momento depois no loop de execução. Se você estiver executando um loop apertado sem interação do usuário e estiver acumulando blocos de liberação automática, acabará ficando sem memory.

Autorelease não é um substituto para a garbage collection (não disponível no SDK do iPhone) e pode levar a erros de ponteiro desagradáveis ​​(o ponteiro ainda parece ser bom, então em algum ponto imprevisível fica inválido), mas também é muito útil para escrever claro e fácil de manter o código. Considere o seguinte caso:

 [aDictionary writeToFile: [documentsDirectory stringByAppendingPathComponent:@"settings.plist"] atomically:YES]; 

A string do caminho é gerada como um object autorelease. Não somos obrigados a criar um object temporário, por isso evitamos essa sobrecarga (e a possibilidade de nos esquecermos de liberá-lo). A memory será totalmente liberada (sem vazamentos), só que isso acontecerá mais tarde no loop de execução. Pergunte a si mesmo: vou alocar centenas deles antes de voltar à input do usuário? Se não (como seria o caso aqui), autorelease é uma ótima solução e, na verdade, esse método NSString para trabalhar com caminhos está disponível apenas usando memory autoreleased.

Eu concordo com o postador acima que seguir a convenção e ser consistente é uma boa ideia.

Eu costumo evitar o uso de autorelease no iPhone onde eu posso (como Jon aponta, você não pode ficar sem ele), simplesmente porque eu gosto de saber que os objects com os quais estou trabalhando são liberados no instante em que eu não preciso deles. Restrições de memory são um dos maiores problemas que você enfrentará no dispositivo e acredito que eles são a fonte da maioria dos problemas que você encontrará por aí.

Como destacado pela Apple, uma área particular de preocupação é quando você usa objects autoreleased dentro de qualquer tipo de loop, porque eles se acumulam dentro do pool de autorelease. Você tem que gerenciar quando drenar o pool ou criar / liberar um. Fazer isso a cada passada pelo loop pode degradar o desempenho, mas a execução de muitos passes pode levar a um uso perigoso da memory. Eu ainda estou aprimorando isso em Moléculas, porque há problemas de memory intermitentes ao importar arquivos de texto grandes (> 2 MB) do Banco de Dados de Proteína. Consegui melhorar o desempenho minimizando os objects autoreleased, mas não consegui eliminá-los completamente.

Outra área a observar é usar objects autoreleased com threads. Se possível, não use objects autoreleased ao lidar com methods executados em um thread de segundo plano, porque o pool pode ser drenado em momentos randoms. Isso leva a falhas intermitentes que podem ser muito divertidas de rastrear.

Eu sugeriria altamente evitar o autorelease como a peste. Os bugs de gerenciamento de memory são uma ótima maneira de gastar muito tempo e dinheiro, eu tive a duvidosa honra de passar pelo processo muitas vezes em antigos aplicativos do Mac, e o fato de que o iPhone tem limitações de memory significa que você tem ser extremamente cuidadoso, ou o aplicativo vai ficar instável e travar com frequência … como muitos dos primeiros aplicativos que foram lançados no último verão.

A única forma confiável que encontrei para escrever aplicativos estáveis ​​do iPhone é gerenciar toda a sua memory e fazer isso de maneira consistente. Mesmo se você for o único programador em seu projeto, agradecerá a si mesmo mais tarde. Pode ser difícil se você aprendeu a programar em idiomas que “cuidam de tudo para você”, mas realmente vale a pena aprender como fazer o melhor se você for sério na criação de aplicativos de qualidade para o iPhone.

    Intereting Posts