Que tipo de vazamento a contagem automática de referência no Objective-C não impede ou minimiza?

235

Nas plataformas Mac e iOS, os vazamentos de memória são geralmente causados ​​por ponteiros não lançados. Tradicionalmente, sempre foi de extrema importância verificar suas alocações, cópias e retenções para garantir que cada uma tenha uma mensagem de liberação correspondente.

A cadeia de ferramentas que acompanha o Xcode 4.2 apresenta a contagem automática de referência (ARC) com a versão mais recente do compilador LLVM , que elimina totalmente esse problema, fazendo com que o compilador gerencie suas coisas para você. Isso é bem legal e reduz muito tempo de desenvolvimento desnecessário e mundano e evita muitos vazamentos de memória descuidados que são fáceis de corrigir com o equilíbrio adequado de retenção / liberação. Mesmo os pools de liberação automática precisam ser gerenciados de maneira diferente quando você habilita o ARC para seus aplicativos Mac e iOS (já que você não deve alocar seus próprios NSAutoreleasePools).

Mas que outras fugas de memória não impede que eu ainda tenha cuidado?

Como bônus, quais são as diferenças entre o ARC no Mac OS X e iOS e a coleta de lixo no Mac OS X?

BoltClock
fonte

Respostas:

262

O principal problema relacionado à memória que você ainda precisa conhecer é o de manter ciclos. Isso ocorre quando um objeto tem um ponteiro forte para outro, mas o objeto de destino tem um ponteiro forte de volta ao original. Mesmo quando todas as outras referências a esses objetos são removidas, elas ainda se mantêm firmes e não serão liberadas. Isso também pode acontecer indiretamente, por uma cadeia de objetos que pode ter o último na cadeia se referindo a um objeto anterior.

É por esta razão que o __unsafe_unretainede __weakqualificadores de propriedade existe. O primeiro não retém nenhum objeto para o qual aponta, mas deixa em aberto a possibilidade desse objeto desaparecer e apontar para uma memória ruim, enquanto o último não retém o objeto e se define automaticamente como zero quando seu destino é desalocado. Dos dois, __weakgeralmente é preferido nas plataformas que o suportam.

Você usaria esses qualificadores para coisas como delegados, nos quais não deseja que o objeto retenha seu delegado e potencialmente leve a um ciclo.

Outro par de preocupações significativas relacionadas à memória são o manuseio de objetos e a memória do Core Foundation alocados usando malloc()para tipos como char*. O ARC não gerencia esses tipos, apenas objetos Objective-C, portanto você ainda precisará lidar com eles. Os tipos de Core Foundation podem ser particularmente difíceis, porque às vezes precisam ser conectados a objetos Objective-C correspondentes e vice-versa. Isso significa que o controle precisa ser transferido para a frente e para trás do ARC ao fazer a ponte entre os tipos de CF e o Objective-C. Algumas palavras-chave relacionadas a essa ponte foram adicionadas, e Mike Ash tem uma ótima descrição de vários casos de ponte em sua extensa redação do ARC .

Além disso, existem vários outros casos menos frequentes, mas ainda potencialmente problemáticos, nos quais a especificação publicada entra em detalhes.

Grande parte do novo comportamento, baseado em manter objetos por perto, desde que haja um forte indicador para eles, é muito semelhante à coleta de lixo no Mac. No entanto, os fundamentos técnicos são muito diferentes. Em vez de ter um processo de coletor de lixo que é executado em intervalos regulares para limpar objetos que não são mais apontados, esse estilo de gerenciamento de memória depende das regras rígidas de retenção / liberação que todos nós precisamos obedecer no Objective-C.

O ARC simplesmente pega as tarefas repetitivas de gerenciamento de memória que tivemos que fazer há anos e as transfere para o compilador, para que nunca mais tenhamos que nos preocupar com elas. Dessa forma, você não tem os problemas de parada ou os perfis de memória do dente de serra experimentados nas plataformas de coleta de lixo. Eu experimentei isso em meus aplicativos Mac coletados pelo lixo e estou ansioso para ver como eles se comportam no ARC.

Para obter mais informações sobre coleta de lixo versus ARC, consulte esta resposta muito interessante de Chris Lattner na lista de correspondência do Objective-C , onde ele lista muitas vantagens do ARC sobre a coleta de lixo do Objective-C 2.0. Encontrei vários dos problemas de GC que ele descreve.

Brad Larson
fonte
2
Obrigado pela resposta detalhada. Eu tive o mesmo problema em que defini um delegado em _unsafe_unretained e tive meu aplicativo travado, corrigido posteriormente alterando para forte, mas agora ele tem um vazamento de memória. Então, mudei para fraco e funciona como um encanto.
chathuram
@ichathura Wow! Você me salvou do lodo da ARC. Eu encontrei a mesma falha ao usar o CMPopTipView.
Nianliang
@ BradLarson: "você não tem problemas de interrupção ou perfis de memória dente de serra experimentados em plataformas de coleta de lixo". Eu esperaria piores perfis de memória de parada e dente de serra da recuperação baseada em escopo e desempenho muito pior da contagem de referências, então eu gostaria de ver uma comparação real.
precisa
Brad, o link de Chris Lattner está morto . Não sou 100%, mas encontrei este outro link. O que eu acho que é o que você queria vincular: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
Honey
1
@ Mel - Obrigado por apontar isso. A que você vincula é um pouco diferente, mas substituí o link morto por uma versão arquivada da mensagem original. Está nos arquivos das listas de discussão, que devem estar disponíveis em algum lugar, mas vou ver se consigo encontrar o novo local.
Brad Larson
14

O ARC não o ajudará com memória não ObjC, por exemplo, se você malloc()precisar, ainda precisa free()disso.

O ARC pode ser enganado performSelector:se o compilador não conseguir descobrir qual é o seletor (o compilador gerará um aviso sobre isso).

O ARC também gerará código seguindo as convenções de nomenclatura da ObjC; portanto, se você combinar o código ARC e o MRC, poderá obter resultados surpreendentes se o código MRC não fizer o que o compilador acha que os nomes prometem.

Listras
fonte
7

Ocorreu um vazamento de memória no meu aplicativo devido aos quatro problemas a seguir:

  1. Não invalidando NSTimers ao descartar controladores de exibição
  2. Esquecer de remover qualquer observador para o NSNotificationCenter ao descartar o controlador de exibição.
  3. Manter fortes referências a si próprio em blocos.
  4. Usando referências fortes para delegados nas propriedades do controlador de exibição

Felizmente, me deparei com a seguinte postagem no blog e consegui corrigi-las: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/

Ed-E G
fonte
0

O ARC também não gerenciará os tipos de CoreFoundation. Você pode 'unir' eles (Usando CFBridgingRelease()), mas apenas se você for usá-lo como um objeto Objective-C / Cocoa. Observe que CFBridgingRelease apenas diminui a contagem de retenção da CoreFoundation em 1 e a move para o ARC da Objective-C.

MaddTheSane
fonte
0

O Xcode 9 fornece uma ótima ferramenta para encontrar esse tipo de problemas. É chamado: " Debug Memory Graph ". Usando-o, você pode encontrar seu objeto vazado por tipo de classe e pode ver claramente quem possui uma forte referência a ele, liberando-o de lá e resolvendo o seu problema. Também detecta ciclos de memória.

Veja mais informações sobre como usá-lo

WILL K.
fonte