Como funciona o novo mecanismo de contagem automática de referências?

Alguém pode me explicar brevemente como funciona o ARC? Eu sei que é diferente da Garbage Collection, mas eu estava me perguntando exatamente como isso funcionava.

Além disso, se o ARC faz o que o GC faz sem prejudicar o desempenho, por que o Java usa o GC? Por que não usa o ARC também?

Todo novo desenvolvedor que chega ao Objective-C precisa aprender as regras rígidas de quando reter, liberar e liberar automaticamente objects. Essas regras até especificam convenções de nomenclatura que implicam a contagem de retenções de objects retornados de methods. O gerenciamento de memory no Objective-C torna-se uma segunda natureza, uma vez que você leve essas regras a sério e as aplique consistentemente, mas mesmo os desenvolvedores de Cocoa mais experientes escorregam de tempos em tempos.

Com o Clang Static Analyzer, os desenvolvedores do LLVM perceberam que essas regras eram confiáveis ​​o suficiente para que pudessem criar uma ferramenta para apontar vazamentos de memory e lançamentos em excesso nos caminhos que seu código leva.

Contagem automática de referência (ARC) é o próximo passo lógico. Se o compilador pode reconhecer onde você deve manter e liberar objects, por que não inserir esse código para você? Tarefas rígidas e repetitivas são o que compiladores e seus irmãos são ótimos. Os humanos esquecem as coisas e cometem erros, mas os computadores são muito mais consistentes.

No entanto, isso não o isenta completamente de se preocupar com o gerenciamento de memory nessas plataformas. Eu descrevo a questão principal a ser observada (reter ciclos) em minha resposta aqui , o que pode exigir um pouco de reflection de sua parte para marcar pointers fracos. No entanto, isso é menor quando comparado ao que você está ganhando no ARC.

Quando comparado ao gerenciamento de memory manual e à garbage collection, o ARC oferece o melhor dos dois mundos eliminando a necessidade de escrever código de reter / liberar, mas não tendo os perfis de memory de parada e dente de serra vistos em um ambiente de garbage collection. As únicas vantagens que a garbage collection tem sobre isso são sua capacidade de lidar com os ciclos de retenção e o fato de que as atribuições de propriedades atômicas são baratas (como discutido aqui ). Eu sei que estou substituindo todo o meu código Mac existente com implementações ARC.

Quanto a se isso poderia ser estendido a outras línguas, parece orientado em torno do sistema de contagem de referência em Objective-C. Pode ser difícil aplicar isso em Java ou outras linguagens, mas eu não sei o suficiente sobre os detalhes do compilador de baixo nível para fazer uma declaração definitiva lá. Dado que a Apple é quem impulsiona esse esforço no LLVM, o Objective-C virá primeiro, a menos que outra parte comporte resources significativos próprios para isso.

O desvelar dos desenvolvedores chocados na WWDC, então as pessoas não estavam cientes de que algo assim poderia ser feito. Pode aparecer em outras plataformas ao longo do tempo, mas por enquanto é exclusivo para LLVM e Objective-C.

O ARC é apenas reproduzir o antigo retentor / release (MRC) com o compilador descobrindo quando chamar reter / liberar. Ele tenderá a ter maior desempenho, menor consumo de memory de pico e desempenho mais previsível do que um sistema de GC.

Por outro lado, alguns tipos de estrutura de dados não são possíveis com o ARC (ou MRC), enquanto o GC pode lidar com eles.

Por exemplo, se você tiver uma class denominada nó, e o nó tiver um NSArray de filhos e uma única referência a seu pai que “simplesmente funciona” com o GC. Com o ARC (e a contagem de referência manual também) você tem um problema. Qualquer nó dado será referenciado por seus filhos e também por seu pai.

Gostar:

A -> [B1, B2, B3] B1 -> A, B2 -> A, B3 -> A 

Tudo está bem enquanto você está usando A (digamos, através de uma variável local).

Quando você terminar (e B1 / B2 / B3), um sistema de GC irá eventualmente decidir olhar para tudo o que puder encontrar a partir dos registradores de pilha e CPU. Ele nunca encontrará A, B1, B2, B3 para finalizá-los e reciclar a memory em outros objects.

Quando você usa ARC ou MRC, e termina com A, tem uma referência de 3 (B1, B2 e B3 fazem referência a ela), e B1 / B2 / B3 terão uma contagem de referência de 1 (NSArray de A contém uma referência a cada). Então, todos esses objects permanecem vivos, embora nada possa usá-los.

A solução comum é decidir que uma dessas referências precisa ser fraca (não contribuir para a contagem de referência). Isso funcionará para alguns padrões de uso, por exemplo, se você referenciar B1 / B2 / B3 somente via A. No entanto, em outros padrões, ele falha. Por exemplo, se você às vezes segurar B1, e esperar subir de volta através do ponteiro pai e encontrar A. Com uma referência fraca se você apenas segurar B1, A pode (e normalmente irá) evaporar, e pegar B2, e B3 com isso.

Às vezes isso não é um problema, mas algumas formas muito úteis e naturais de trabalhar com estruturas complexas de dados são muito difíceis de usar com o ARC / MRC.

Então, o ARC tem como alvo o mesmo tipo de problemas de metas do GC. No entanto ARC trabalha em um conjunto mais limitado de padrões de uso do GC, então se você pegasse uma linguagem GC (como Java) e enxertasse algo como ARC nela, alguns programas não funcionariam mais (ou pelo menos gerariam toneladas de memory abandonada e pode causar sérios problemas de troca ou ficar sem memory ou trocar espaço).

Você também pode dizer que o ARC coloca uma prioridade maior no desempenho (ou talvez na previsibilidade), enquanto o GC coloca uma prioridade maior em ser uma solução genérica. Como resultado, o GC tem demandas de CPU / memory menos previsíveis e um desempenho mais baixo (normalmente) do que o ARC, mas pode manipular qualquer padrão de uso. O ARC funcionará muito melhor para muitos padrões de uso comuns, mas para alguns padrões de uso (válidos!) Ele irá cair e morrer.

Magia

Mas, mais especificamente, o ARC funciona fazendo exatamente o que você faria com seu código (com algumas pequenas diferenças). O ARC é uma tecnologia de tempo de compilation, ao contrário do GC, que é tempo de execução e afetará negativamente seu desempenho. O ARC rastreará as referências aos objects para você e sintetizará os methods de retenção / liberação / liberação automática de acordo com as regras normais. Por causa disso, o ARC também pode liberar as coisas assim que elas não são mais necessárias, ao invés de lançá-las em um pool de autorelease puramente por causa da convenção.

Algumas outras melhorias incluem o zeramento de referências fracas, cópia automática de blocos para o heap, speedups através da placa (6x para pools autorelease!).

Uma discussão mais detalhada sobre como tudo isso funciona é encontrada no LLVM Docs no ARC.

Isso varia muito da garbage collection. Você viu os avisos que dizem que você pode estar vazando objects em linhas diferentes? Essas declarações até informam em qual linha você alocou o object. Isso foi levado um passo adiante e agora podemos inserir instruções de retain / release nos locais apropriados, melhor do que a maioria dos programadores, quase 100% do tempo. Ocasionalmente, existem alguns exemplos estranhos de objects retidos que você precisa para ajudá-lo.

Muito bem explicado pela documentação do desenvolvedor da Apple. Leia “Como funciona o ARC”

Para garantir que as instâncias não desapareçam enquanto ainda são necessárias, o ARC controla quantas propriedades, constantes e variables ​​estão se referindo atualmente a cada instância da class. O ARC não desalocará uma instância, desde que pelo menos uma referência ativa a essa instância ainda exista.

Para garantir que as instâncias não desapareçam enquanto ainda são necessárias, o ARC controla quantas propriedades, constantes e variables ​​estão se referindo atualmente a cada instância da class. O ARC não desalocará uma instância, desde que pelo menos uma referência ativa a essa instância ainda exista.

Para conhecer o Diff. entre Coleta de lixo e ARC: Leia isto