Gerenciamento de memória

A primeira pergunta da maioria dos desenvolvedores Java é como o gerenciamento de memória é implementado pelo J2ObjC, já que o Java tem coleta de lixo e o Objective-C não por padrão. O iOS tem dois métodos de gerenciamento de memória: contagem de referência e contagem automática de referência (ARC, na sigla em inglês).

O J2ObjC gera um código de gerenciamento de memória diferente, dependendo do método escolhido. Traduza com a opção -use-arc para gerar um código que usa o ARC. Por padrão, ele usa a contagem manual de referências.

Contagem de referência

O método de contagem de referência torna a propriedade do objeto explícita. Um método é proprietário de um objeto quando o cria, até que ele libere esse objeto. Ao receber um objeto de outro método, o método de recebimento o retém caso precise ser referenciado após o retorno do método. Quando um método não precisa mais referenciar um objeto, ele precisa liberá-lo. Quando a contagem de retenções de um objeto é zero, a memória dele é liberada e o objeto não é mais válido. Quando os objetos são liberados, o método desaloc() é chamado para liberar a propriedade das variáveis de instância deles.

Um problema com essa técnica é como transferir a propriedade de um objeto. Por exemplo, um método de fábrica pode ser chamado para criar um objeto. Se o método de fábrica liberar o objeto antes de retorná-lo (já que ele não quer mais ser proprietário do objeto), esse objeto será liberado antes que o método de chamada possa retê-lo.

Para transferir a propriedade de um objeto, um método envia a ele uma mensagem de lançamento automático (em vez de uma mensagem de lançamento), que adia a mensagem de lançamento. Isso permite que o método de fábrica crie um objeto a ser retornado e renuncia à propriedade sem invalidá-lo. Em intervalos regulares, como após cada iteração de loop de eventos em um aplicativo iOS, o pool de lançamento automático é "drenado", o que significa que todos os objetos nesse pool recebem as mensagens de lançamento adiada. Todos os objetos em que a contagem de retenção cai para zero são liberados normalmente.

Como a carga no gerenciamento de memória é para o desenvolvedor, é fácil vazar memória com o método de contagem de referência. No entanto, a Apple recomenda algumas práticas recomendadas para minimizar esse problema, implementadas pelo J2ObjC.

Há também suporte a tempo de execução e ferramentas para detectar vazamentos de memória. O ambiente de execução do Objective-C relata vazamentos detectados quando um aplicativo é encerrado, e esse é um motivo pelo qual o J2ObjC converte os testes do JUnit em binários executáveis. O Xcode usa o Clang, e esse compilador oferece excelente análise estática de problemas de memória, que o Xcode disponibiliza com o comando "Analyze".

Contagem de referência automática (ARC)

ARC é o método de gerenciamento de memória recomendado pela Apple. Ela transfere a responsabilidade da contagem de referência para o compilador, que adiciona os métodos adequados de retenção, liberação e liberação automática durante a compilação. O ARC oferece suporte a referências fracas para dispositivos com iOS 5 e versões mais recentes.

Recomendamos que os projetos usem o ARC para o código traduzido. O código Objective-C transcompilado é como o código Objective-C escrito à mão. O uso do ARC pode ajudar os desenvolvedores a evitar erros comuns relacionados à memória, como superliberação ou subreferência, tornando o gerenciamento de memória mais simples e menos propenso a erros.

Por padrão, ARC não é seguro para exceções. Especificamente, ele vaza memória quando exceções são geradas. Como as exceções são mais comuns no Java e geralmente podem ser recuperadas, isso pode ser problemático. O uso de -fobjc-arc-exceptions ao compilar com o arc corrige os vazamentos com um custo de desempenho aceitável.

Também recomendamos que novos projetos usem o ARC para o código Objective-C escrito à mão e só substituam a contagem manual de referências se os dados de criação de perfil mostrarem um problema de desempenho real. Códigos ARC e não ARC podem ser compilados e vinculados ao mesmo app sem problemas.

Referências fracas

Os campos podem ser anotados com com.google.devtools.j2objc.Weak, que o transpilador usa para gerar campos que seguem a semântica de referência fraca do Objective-C. O uso da contagem de referência significa que o campo não é mantido quando inicializado e é liberado automaticamente quando a instância que o contém é liberada. Com o ARC, os campos fracos são marcados com a anotação __unsafe_unretained, e as propriedades relacionadas são declaradas como fracas.

Em alguns casos, uma instância de classe interna entra em um ciclo de referência com a instância externa. Aqui, uma anotação com.google.devtools.j2objc.WeakOuter é usada para marcar a classe interna. Assim, a referência à classe externa é tratada conforme descrito acima. Outros campos na classe interna não são afetados por essa anotação.

O J2ObjC também oferece suporte à classe WeakReference. Portanto, o código Java que a usa funcionará da mesma maneira quando traduzido. Esteja ciente de que WeakReference é inerentemente não determinístico na JVM. Os aplicativos que querem evitar vazamentos de memória enquanto mantem a previsibilidade precisam preferir @Weak e @WeakOuter.

Ferramentas de gerenciamento de memória

  • Cycle Finder Tool: analisa os arquivos de origem Java em busca de ciclos fortes de referência de objetos.
  • Xcode Instruments: o pacote de ferramentas de criação de perfil do Xcode.
  • Xcode Memory Diagnostics: opções de build para execução com diagnósticos e geração de registros de memória.