Gerenciamento de memória Obj-C

O gerenciamento de memória é um dos processos mais importantes em qualquer linguagem de programação. É o processo pelo qual a memória de objetos é alocada quando são necessários e desalocada quando não são mais necessários.

Gerenciar a memória do objeto é uma questão de desempenho; se um aplicativo não liberar objetos desnecessários, sua área de cobertura de memória aumentará e o desempenho será prejudicado.

As técnicas de gerenciamento de memória Objective-C podem ser amplamente classificadas em dois tipos.

  • "Manual Retain-Release" ou MRR
  • "Contagem automática de referência" ou ARC

"Manual Retain-Release" ou MRR

No MRR, gerenciamos explicitamente a memória, controlando os objetos por conta própria. Isso é implementado usando um modelo, conhecido como contagem de referência, que a classe Foundation NSObject fornece em conjunto com o ambiente de tempo de execução.

A única diferença entre o MRR e o ARC é que a retenção e a liberação são feitas por nós manualmente no primeiro, enquanto no segundo é feito automaticamente.

A figura a seguir representa um exemplo de como o gerenciamento de memória funciona em Objective-C.

O ciclo de vida da memória do objeto Classe A é mostrado na figura acima. Como você pode ver, a contagem de retenção é mostrada abaixo do objeto, quando a contagem de retenção de um objeto torna-se 0, o objeto é liberado completamente e sua memória é desalocada para outros objetos usarem.

O objeto Classe A é criado primeiro usando o método aloc / init disponível em NSObject. Agora, a contagem de retenção torna-se 1.

Agora, a classe B retém o objeto da Classe A e a contagem retida do objeto da Classe A torna-se 2.

Em seguida, a Classe C faz uma cópia do objeto. Agora, ele é criado como outra instância da Classe A com os mesmos valores para as variáveis ​​de instância. Aqui, a contagem de retenção é 1 e não a contagem de retenção do objeto original. Isso é representado pela linha pontilhada na figura.

O objeto copiado é liberado pela Classe C usando o método de liberação e a contagem de retenção torna-se 0 e, portanto, o objeto é destruído.

No caso do objeto Classe A inicial, a contagem de retenção é 2 e ele deve ser liberado duas vezes para ser destruído. Isso é feito por declarações de liberação de Classe A e Classe B, que diminuem a contagem de retenção para 1 e 0, respectivamente. Finalmente, o objeto é destruído.

Regras básicas de MRR

  • Nós possuímos qualquer objeto que criamos: criamos um objeto usando um método cujo nome começa com "alloc", "new", "copy" ou "mutableCopy"

  • Podemos assumir a propriedade de um objeto usando reter: Um objeto recebido normalmente tem garantia de permanecer válido dentro do método em que foi recebido, e esse método também pode retornar com segurança o objeto para seu invocador. Usamos reter em duas situações -

    • Na implementação de um método acessador ou método init, para assumir a propriedade de um objeto que queremos armazenar como um valor de propriedade.

    • Para evitar que um objeto seja invalidado como efeito colateral de alguma outra operação.

  • Quando não precisarmos mais dele, devemos renunciar à propriedade de um objeto que possuímos: Renunciamos à propriedade de um objeto enviando-lhe uma mensagem de liberação ou uma mensagem de liberação automática. Na terminologia do Cocoa, renunciar à propriedade de um objeto é, portanto, normalmente referido como "liberação" de um objeto.

  • Você não deve renunciar à propriedade de um objeto que não seja seu: isso é apenas o corolário das regras de política anteriores declaradas explicitamente.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");
   
   // Should set the object to nil
   sampleClass = nil;
   return 0;
}

Quando compilarmos o programa acima, obteremos a seguinte saída.

2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this

"Contagem automática de referência" ou ARC

Na contagem automática de referência ou ARC, o sistema usa o mesmo sistema de contagem de referência do MRR, mas insere as chamadas de método de gerenciamento de memória apropriadas para nós em tempo de compilação. Somos fortemente encorajados a usar o ARC para novos projetos. Se usarmos o ARC, normalmente não há necessidade de entender a implementação subjacente descrita neste documento, embora possa ser útil em algumas situações. Para obter mais informações sobre o ARC, consulte Transitioning to ARC Release Notes.

Conforme mencionado acima, no ARC, não precisamos adicionar métodos de liberação e retenção, pois isso será feito pelo compilador. Na verdade, o processo subjacente de Objective-C ainda é o mesmo. Ele usa as operações de retenção e liberação internamente tornando mais fácil para o desenvolvedor codificar sem se preocupar com essas operações, o que reduzirá a quantidade de código escrito e a possibilidade de vazamentos de memória.

Havia outro princípio chamado coleta de lixo, que é usado no Mac OS-X junto com o MRR, mas desde sua depreciação no OS-X Mountain Lion, não foi discutido junto com o MRR. Além disso, os objetos iOS nunca tiveram o recurso de coleta de lixo. E com o ARC, não há uso de coleta de lixo no OS-X também.

Aqui está um exemplo simples de ARC. Observe que isso não funcionará no compilador online, pois não oferece suporte a ARC.

#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

Quando compilarmos o programa acima, obteremos a seguinte saída.

2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated