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