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


206

Alguém pode me explicar brevemente como o ARC funciona? Sei que é diferente da Garbage Collection, mas estava me perguntando exatamente como 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?


2
Isso mostrará tudo: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Como é implementado no Xcode e no iOS 5, está sob NDA.
Morten Fast

14
@mbehan Isso é um péssimo conselho. Não quero fazer login ou ter uma conta no iOS Dev Center, mas estou interessado em saber sobre o ARC.
Andres F.

1
O ARC não faz tudo o que o GC faz, exige que você trabalhe com semântica de referência forte e fraca explicitamente e vaza memória se você não acertar. Na minha experiência, isso é inicialmente complicado quando você usa blocos no Objective-C, e mesmo depois de aprender os truques, você fica com algum código irrelevante (IMO) em torno de muitos usos de blocos. É mais conveniente esquecer as referências fortes / fracas. Além disso, o GC pode ter um desempenho um pouco melhor que o ARC wrt. CPU, mas requer mais memória. Pode ser mais rápido que o gerenciamento explícito de memória quando você tem muita memória.
TaylanUB

@ TaylanUB: "requer mais memória". Muitas pessoas dizem isso, mas acho difícil de acreditar.
precisa saber é o seguinte

2
@ JonHarrop: Atualmente, eu nem me lembro por que disse isso, para ser honesto. :-) Enquanto isso, percebi que existem tantas estratégias diferentes de GC que tais declarações gerais provavelmente não valem nada. Deixe-me recitar Hans Boehm de seus mitos e meias verdades de alocação de memória : "Por que essa área é tão propensa a sabedorias folclóricas duvidosas?"
TaylanUB

Respostas:


244

Todo novo desenvolvedor que chega ao Objective-C precisa aprender as regras rígidas de quando reter, liberar e liberar objetos automaticamente. Essas regras até especificam convenções de nomenclatura que implicam a contagem de retenção de objetos retornados dos métodos. O gerenciamento de memória no Objective-C se torna uma segunda natureza quando você leva essas regras a sério e as aplica de maneira consistente, mas mesmo os desenvolvedores de cacau mais experientes desaparecem de tempos em tempos.

Com o Clang Static Analyzer, os desenvolvedores do LLVM perceberam que essas regras eram confiáveis ​​o suficiente para criar uma ferramenta para apontar vazamentos de memória e liberações excessivas nos caminhos que seu código segue.

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

No entanto, isso não o libera completamente de se preocupar com o gerenciamento de memória nessas plataformas. Descrevo a questão principal a ser observada (reter ciclos) na minha resposta aqui , o que pode exigir um pouco de sua parte para marcar indicadores fracos. No entanto, isso é menor quando comparado ao que você está ganhando no ARC.

Quando comparado ao gerenciamento manual de memória e coleta de lixo, o ARC oferece o melhor dos dois mundos, eliminando a necessidade de escrever um código de retenção / liberação, mas sem os perfis de memória de parada e dente de serra vistos em um ambiente de coleta de lixo. As únicas vantagens da coleta de lixo são a capacidade de lidar com ciclos de retenção e o fato de as atribuições de propriedades atômicas serem baratas (como discutido aqui ). Sei que estou substituindo todo o meu código existente do Mac GC por implementações ARC.

Quanto a isso poder ser estendido para outros idiomas, parece estar voltado para o sistema de contagem de referência no Objective-C. Pode ser difícil aplicar isso ao Java ou a outras linguagens, mas não sei o suficiente sobre os detalhes do compilador de baixo nível para fazer uma declaração definitiva. Dado que a Apple está impulsionando esse esforço no LLVM, o Objective-C virá primeiro, a menos que outra parte comprometa recursos significativos próprios para isso.

A inauguração desses desenvolvedores chocou a 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.


56
grifo meu: isto não completamente livrá-lo de se preocupar com o gerenciamento de memória
bshirley

6
O ARC é realmente uma inovação? Pela sua resposta, concluo que o ARC é um novo conceito, usado no Objective-C pela primeira vez (corrija-me se estiver errado). Para ser sincero, não sou desenvolvedor de Objective-C e não sei muito sobre o ARC, mas os Boost Shared Pointers (consulte boost.org) não são exatamente a mesma coisa? E se não estiverem, qual é a diferença?
theDmi

2
@DMM - Em vez de depender de operadores sobrecarregados (como o Boost), esse é um processo no nível do compilador, que o estende por todo o idioma. Entre outras coisas, isso facilita a conversão de um aplicativo contado de referência manual para o ARC. O Boost também pode manipular variáveis ​​locais de maneira diferente do ARC, onde o ARC sabe o momento em que uma variável local não está mais sendo usada e pode liberar nesse ponto. Acredito que, com o Boost, você ainda precisa especificar de alguma forma que você terminou a variável.
Brad Larson

6
Para responder à pergunta "é novo", o Delphi possui contagem automática de referências para strings, matrizes e interfaces (para suporte COM) há mais de uma década. Eu concordo que é realmente um bom compromisso entre um ambiente de GC e um ambiente "faça tudo manualmente". Fico feliz que esteja no ObjC e no LLVM (para que outros idiomas também possam tirar proveito).
Davidmw

2
@theDmi: "O ARC é realmente uma inovação?". A contagem automática de referência foi inventada em 1960 e tem sido usada em muitas línguas, como Python e Mathematica. Não é usado na JVM ou CLR porque é muito lento e vaza ciclos.
precisa saber é o seguinte

25

O ARC é apenas reproduzir a retenção / liberação antiga (MRC) com o compilador descobrindo quando chamar reter / liberar. Ele tenderá a ter um desempenho mais alto, menor uso de memória de pico e desempenho mais previsível 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.

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

Gostar:

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

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

Quando você terminar com ele (e B1 / B2 / B3), um sistema de GC decidirá, eventualmente, examinar tudo o que encontrar, iniciando nos registros da pilha e da CPU. Ele nunca encontrará A, B1, B2, B3, finalizando-os e reciclando a memória em outros objetos.

Quando você usa ARC ou MRC, e termina com A, ele tem uma refcount de 3 (B1, B2 e B3 todos fazem referência a ele), e B1 / B2 / B3 terá todos uma contagem de referência de 1 (o NSArray de A mantém uma referência a cada). Portanto, todos esses objetos permanecem vivos, mesmo que nada possa usá-los.

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

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

Portanto, o ARC visa o mesmo tipo de problemas que o GC almeja. No entanto, o ARC trabalha com um conjunto mais limitado de padrões de uso que o GC; portanto, 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 memória abandonada) , e pode causar sérios problemas de troca ou ficar sem memória ou trocar espaço).

Você também pode dizer que o ARC coloca uma prioridade maior no desempenho (ou talvez previsibilidade), enquanto o GC coloca uma prioridade maior em ser uma solução genérica. Como resultado, o GC possui demandas de CPU / memória menos previsíveis e um desempenho mais baixo (normalmente) que o ARC, mas pode lidar com 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 cairá e morrerá.


"Por outro lado, alguns tipos de estrutura de dados não são possíveis com o ARC". Acho que você quis dizer que a limpeza automática não é possível sem dicas; obviamente, as estruturas de dados são.
Steven Fisher

Claro, mas APENAS a limpeza automática de objetos ObjC está disponível no ARC, portanto "sem limpeza automática" == "sem limpeza". Vou reformular e responder quando tiver mais tempo.
Stripes

@ Stripes: o equivalente à limpeza manual no ARC é a quebra manual de ciclos, por exemplo foo = nil.
Douglas

"[ARC] tenderá a ter um desempenho mais alto ... O ARC coloca uma prioridade maior no desempenho". Fico surpreso ao ler que, quando se sabe, a contagem de referências é muito mais lenta do que rastrear a coleta de lixo. flyingfrogblog.blogspot.co.uk/2011/01/...
Jon Harrop

2
Em teoria, o GC é mais rápido (cada manipulação de contagem de referência deve ser coerente com o cache do multiprocessador, e existem muitos). Na prática, o único sistema de GC disponível para ObjC é muito mais lento. Também é extremamente comum que os sistemas de GC pausem encadeamentos aleatoriamente por períodos perceptíveis pelo usuário (existem alguns sistemas de GC em tempo real, mas eles não são comuns, e acho que eles têm restrições "interessantes")
Stripes

4

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 compilação, diferente do GC, que é de tempo de execução e afetará negativamente seu desempenho. O ARC rastreará as referências a objetos para você e sintetizará os métodos de retenção / liberação / autorelease de acordo com as regras normais. Por esse motivo, o ARC também pode liberar as coisas assim que elas não forem mais necessárias, em vez de jogá-las em um pool de liberação automática apenas para fins de convenção.

Algumas outras melhorias incluem zerar referências fracas, cópia automática de blocos para a pilha, acelerações em geral (6x para pools de liberação automática!).

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


2
-1 "O ARC é uma tecnologia de tempo de compilação, diferente do GC, que é de tempo de execução e afetará negativamente seu desempenho". As contagens de referência são aumentadas em tempo de execução, o que é muito ineficiente. É por isso que rastrear GCs como JVM e .NET é muito mais rápido.
precisa saber é o seguinte

1
@ Jon: Você tem uma prova disso? Pela minha própria leitura, parece que novos algoritmos de RC normalmente têm um desempenho tão bom ou melhor que o M&S GC.
Xryl669

1
@ xryl669: Há uma explicação completa no Manual do GC ( gchandbook.org ). Observe que rastreio! = M&S.
Jon Harrop

3

Varia muito da coleta de lixo. Você já viu os avisos que indicam que você pode estar vazando objetos em linhas diferentes? Essas instruções até dizem em qual linha você alocou o objeto. Isso deu um passo adiante e agora pode inserir retain/ releaseinstruções nos locais apropriados, melhores do que a maioria dos programadores, quase 100% do tempo. Ocasionalmente, existem algumas instâncias estranhas de objetos retidos com as quais você precisa ajudá-lo.


0

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

Para garantir que as instâncias não desapareçam enquanto ainda são necessárias, o ARC rastreia quantas propriedades, constantes e variáveis ​​estão atualmente se referindo a cada instância de classe. O ARC não desalocará uma instância enquanto pelo menos uma referência ativa a essa instância ainda existir.

Para garantir que as instâncias não desapareçam enquanto ainda são necessárias, o ARC rastreia quantas propriedades, constantes e variáveis ​​estão atualmente se referindo a cada instância de classe. O ARC não desalocará uma instância enquanto pelo menos uma referência ativa a essa instância ainda existir.

Conhecer Diff. entre coleta de lixo e ARC: Leia isto


0

O ARC é um recurso do compilador que fornece gerenciamento automático de memória de objetos.

Em vez de você precisar se lembrar de quando usar retain, release, o autoreleaseARC avalia os requisitos de vida útil de seus objetos e insere automaticamente as chamadas de gerenciamento de memória apropriadas para você no momento da compilação. O compilador também gera métodos de desalocação apropriados para você.

O compilador insere as retain/releasechamadas necessárias em tempo de compilação, mas essas chamadas são executadas em tempo de execução, como qualquer outro código.

O diagrama a seguir forneceria uma melhor compreensão de como o ARC funciona.

insira a descrição da imagem aqui

Aqueles que são novos no desenvolvimento do iOS e não têm experiência de trabalho no Objetivo C. Consulte a documentação da Apple para o Advanced Memory Management Programming Guide para entender melhor o gerenciamento de memória.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.