Controle de Memória no iOS

Sabe quando você está utilizando um aplicativo no seu iPhone e ele começa a ficar lento, travando e de repente o aplicativo fecha? Então, provavelmente ocorreu um problema conhecido como vazamento de memória. Para saber um pouco mais sobre esse tipo problema no iOS, confira o artigo.

Ciclo de vida de uma referência

Para entender o uso da memória no iOS, é fundamental entender como funciona o ciclo de vida de uma referência a uma classe. Esse ciclo compreende basicamente 5 etapas. São elas:

1. Alocação: nessa etapa é fornecido um espaço da memória do dispositivo, para que a referência possa ocupar;

2. Inicialização: em seguida, com o seu espaço garantido, o objeto pode ser inicializado pelo aplicativo;

3. Uso: agora, o objeto pode ser utilizado todas as vezes que for necessário;

4. Desinicialização: ao terminar de usar a referência, o objeto é desinicializado, mas continua ocupando o espaço na memória;

5. Desalocação: nessa última etapa, o espaço da memória é liberado, e pode ser usado para outras necessidades.

Vou utiizar uma analogia como exemplo. Imagine que você chegou em um restaurante e pediu uma mesa para o garçom. Nesse momento, estará sendo reservado um lugar para você se sentar, mesmo que você ainda não tenha sentado (Alocação). Em seguida, você se desloca até a mesa disponibilizada para você e se senta nela, ocupando o seu espaço (Inicialização). Durante um tempo, você estará usando essa mesa e pedindo tudo que tem direito (Uso). Após isso, você pagará a conta e sairá da mesa, mas ela ainda não vai estar disponível para uso, uma vez que ainda estará suja (Desinicialização). Então, o garçom volta à mesa para limpá-la e assim a deixa pronta para uso do próximo cliente (Desalocação).

 

Automatic Reference Counting (ARC)

O ARC é um contador automático de referências presente nas linguagens Swift e Objective-C. Com ele, toda vez que uma referência a uma classe é feita, um espaço de memória é destinado para armazenar informações sobre ela, e a cada referência a ela, uma unidade é incrementada ao seu contador automático. A cada referência tirada desse objeto, seu contador diminui, e quando não existirem mais referências a essa classe, seu contador retorna a zero. Assim o objeto é desalocado, parando de ocupar espaço na memória.

No exemplo a seguir, a classe Compasser possui um atributo name, um método de inicialização e um de desinicialização.

Ao criar as variáveis compasser 1, 2 e 3 do tipo Compasser como opcionais, inicialmente seus valores são dados como nulos e nenhum valor é incrementado ao ARC.

Em seguida, a classe Compasser é inicializada juntamente com a contagem do ARC.

E a seguinte mensagem aparece no console.

Ao atribuir valores para as variáveis compasser 2 e 3 iguais ao da “compasser1”, a contagem do ARC aumenta para 3.

Para diminuir a contagem do ARC as variáveis são dadas como nulas. Porém, enquanto existir referências para a classe e o ARC não for nulo, a variável não é desalocada da memória.

Assim que a última referência é dada como nula, o contador alcança o zero. A classe pode ser desinicializada da memória.

Quando isso ocorre, o desinicializador é chamado e a seguinte mensagem é impressa no console. Assim, a variável é desalocada da memória.

Apesar de parecer que com o ARC a memória não fica sendo ocupada com objetos que não são mais utilizados, problemas como a referência cíclica forte fazem com que o objeto fique inacessível, ocorrendo o vazamento de memória.

 

Referência do tipo Strong

Antes de aprender sobre a referência cíclica, é fundamental entender as referências do tipo forte. Elas ocorrem quando não se especifica nada antes da declaração da variável. Ao utilizar referências fortes, a classe só é desalocada quando não existirem mais referências ao objeto, como no exemplo a seguir.

Duas classes são declaradas, a Pet e a Human, sendo que esta possui como atributo forte Pet. Assim, sempre que a classe Human for inicializada, a classe Pet também será.

Inicialmente, declaramos a variável pedro inicializando a classe Human, assim, a Pet é inicializada como mostra o console.

Nesse caso, o ARC de Human e Pet sobem para 1. A seguir, uma ilustração para que fique mais fácil de visualizar.

Em seguida, declara-se pedro como nulo, fazendo com que sua contagem seja 0. Uma vez que não há mais referências à classe Human, ela é desalocada, e consequentemente Pet também.

A partir desse exemplo, foi possível compreender como funciona a referência do tipo forte. Agora ficará mais fácil para entender como acontece a referência cíclica.

 

Referência Cíclica Forte

A referência cíclica ocorre quando dois objetos são inicializados separadamente, e em um dado momento esses objetos apontam um para o outro com referências do tipo forte. Após esses objetos não terem mais referências individuais apontando para eles, continuam tendo essas referências entre si. Com isso, os objetos ficam inacessíveis e não podem ser desalocados. Parece meio confuso de entender, não é mesmo? Para entender melhor, veja o exemplo a seguir.

Nesse exemplo são criadas duas classes, Student, que contém name e favoriteBook como atributos, e a classe Book, que contém seu title e owner como atributos. Ambas possuem os métodos de inicialização e de desinicialização.

Em seguida, as variáveis maria e theLittlePrince são criadas, e, por serem opcionais e não terem sido inicializadas, a contagem do ARC ainda não foi incrementada.

A partir do momento que é atribuído um valor às variáveis, as classes Student e Book são inicializadas, como mostra o console.

A partir da ilustração, é possível ver as referências para cada classe e cada seta incrementa o ARC.

Em seguida, é atribuído um favoriteBook para maria e uma owner ao livro theLittlePrince. Assim, referências do tipo forte são criadas para cada classe e, logo, o ARC é incrementado.

Nota-se que maria aponta para theLittlePrince e theLittlePrince também aponta para maria.

Ao atribuir o valor nulo para as variáveis maria e theLittlePrince, o ARC decresce para um, mas não chega a 0, pois continua tendo a referência de theLittlePrince para maria, e maria para theLittlePrince, como mostra a figura.

Esse tipo de referência faz com que o ARC nunca chegue a zero, tornando as referências órfãs e inacessíveis, mesmo que elas continuem ocupando espaço na memória.

 

Referências do tipo Weak

As referências fracas permitem que haja relação entre duas classes, mas não aumenta a contagem do ARC, assim o objeto pode ser desalocado, mesmo que existam referências fracas apontando para ele. A forma de indicar que uma referência é fraca, é colocar a palavra “weak” antes da declaração da variável.

Uma característica marcante desse tipo de referência, é que ela não necessariamente deverá existir, sendo declarada como opcional. Por esse motivo, ela deverá sempre ser uma variável (var) e não uma constante (let).

O exemplo da referência fraca é o mesmo da referência cíclica, alterando apenas o tipo de referência da variável owner para weak.

As variáveis maria e theLittlePrince são criadas e em seguida inicializadas com seu nome e título, respectivamente. Ao declarar o favoriteBook de maria e o owner de theLittlePrince, as variáveis maria e theLittlePrince ficam ligadas entre si.

Na ilustração é possível ver como fica a relação entre Student e Book. Sendo a referência de Student para Book forte, o sentido contrário é fraco. Assim, o ARC de Student é igual a 1 e o ARC de Book é igual a 2.

Agora, ao declarar maria como nula, ela pode ser desinicializada, e a contagem do ARC para Book diminui para 1.

Em seguida, declara-se theLittlePrince como nulo seu ARC chega a 0, imprimindo a seguinte mensagem no console.

Assim, é possível perceber que o problema de referência cíclica foi resolvido, apenas colocando uma palavra antes da declaração da variável.

 

Referências do tipo Unowned

Uma referência unowned, traduzindo para o português, sem dono, é muito parecida com a referência fraca, pois também não incrementa a contagem do ARC. Porém, ao contrário da referência fraca, uma referência sem dono sempre vai receber um valor, então não pode ser opcional.

Por isso, o exemplo de uma referência sem dono, é o caso de uma classe Person e Email, pois uma pessoa pode existir sem um email (por isso o optional), porém um email não pode existir sem uma pessoa associada a ele.

Note que por cada classe referenciar uma à outra, as chances de uma referência cíclica ocorrer são altas. Para evitar isso, usa-se a palavra “unowned” antes do atributo person, já que ele sempre vai existir.

Declaramos a variável ana como opcional para que no final ela possa ser dada como nula e diminua a contagem do ARC.

Em seguida, inicializamos a classe Person e atribuímos um email para ana, inicializando Email consequentemente.

Assim que Person e Email são inicializadas, as seguintes mensagens aparecem no console.

A ilustração a seguir facilita a compreensão de como as referências estão funcionando.

Em seguida, declaramos ana como nula para que não existam mais referências fortes apontando para Person, assim a classe pode ser desinicializada. Quando isso acontece, a classe Email também perde a referência que aponta para ela e também é desinicializada.

A seguinte mensagem aparece no console quando ana é declarada como nula.

A ilustração a seguir representa a classe Person sem referências fortes apontando para ela, logo antes de ser desalocada.

Uma vez que a classe Person é desalocada, a classe Email também fica sem referências fortes apontando para ela, sendo desalocada da memória. Mais uma vez o problema de referência cíclica foi resolvido.

Espero que esse artigo tenha te ajudado a entender melhor como resolver problemas de memória no iOS. Para mais dicas, continue visitando nossa página.

Gostou da solução? Nós podemos ajudar!

Conheça nossos conteúdos gratuitos, direcionados aos assuntos de sua preferência!

Enviar

Receba nosso conteúdo

Gostaria de receber de forma gratuita mais conteúdos sobre este ou outros assuntos? Preencha o formulário abaixo e receba nosso conteúdo gratuito!

Parabéns!

Você receberá nosso conteúdo em breve!

Atenção

Tivemos um problema com seu formulário, tente novamente.