Lição 40: Gerenciamento de Memória e Referências Fortes vs Fracas
O gerenciamento de memória em Swift é um tópico essencial que todo desenvolvedor deve entender para escrever aplicativos eficientes e seguros. Nesta lição, discutiremos os conceitos de referências fortes e fracas, como elas afetam o gerenciamento de memória e quando usar cada tipo de referência.
Visão Geral do Gerenciamento de Memória
Swift utiliza o Contador Automático de Referências (ARC) para gerenciar a memória. O ARC ajuda a acompanhar o número de referências para instâncias de classes. Quando não há mais referências a uma instância, o ARC desaloca automaticamente a memória usada por essa instância.
Referências Fortes
Em Swift, uma referência forte é o tipo de referência padrão. Quando você cria uma referência forte para um objeto, isso impede que esse objeto seja desalocado. A contagem de referências aumenta em 1, e o objeto permanecerá na memória enquanto houver pelo menos uma referência forte apontando para ele.
Veja um exemplo demonstrando referências fortes:
class Pessoa {
let nome: String
init(nome: String) {
self.nome = nome
print("\(nome) foi inicializado.")
}
deinit {
print("\(nome) foi desalocado.")
}
}
var joao: Pessoa? = Pessoa(nome: "João da Silva")
// Saída: João da Silva foi inicializado.
joao = nil
// Saída: João da Silva foi desalocado.
No código acima, a instância de Pessoa
é desalocada quando não há mais referências fortes para ela.
Referências Fracas
Uma referência fraca, por outro lado, não aumenta a contagem de referências. Se as únicas referências a uma instância forem fracas, essa instância ainda poderá ser desalocada. As referências fracas são normalmente usadas para evitar ciclos de retenção, que podem levar a vazamentos de memória.
Para declarar uma referência fraca, você usa a palavra-chave weak
. Referências fracas devem sempre ser de tipos opcionais (T?
) porque podem se tornar nil quando o objeto referenciado é desalocado.
Veja um exemplo que ilustra referências fracas:
class Professor {
let nome: String
init(nome: String) {
self.nome = nome
print("\(nome) foi inicializado.")
}
deinit {
print("\(nome) foi desalocado.")
}
}
class Aluno {
let nome: String
weak var professor: Professor? // Usa referência fraca para evitar ciclo de retenção
init(nome: String) {
self.nome = nome
print("\(nome) foi inicializado.")
}
deinit {
print("\(nome) foi desalocado.")
}
}
var professorMatematica: Professor? = Professor(nome: "Sr. Silva")
var aluno: Aluno? = Aluno(nome: "Alice")
aluno?.professor = professorMatematica
aluno = nil
// Saída: Alice foi desalocado.
professorMatematica = nil
// Saída: Sr. Silva foi desalocado.
Neste exemplo, a classe Aluno
tem uma referência fraca para a classe Professor
. Quando a instância de Aluno
é definida como nil
, ela não mantém a instância de Professor
na memória, permitindo que ambas sejam desalocadas corretamente.
Referências Não Propriedades (Unowned References)
Uma referência não própria é semelhante a uma referência fraca, mas assume que o objeto referenciado nunca será desalocado enquanto a referência ainda estiver em escopo. Portanto, você não precisa declará-la como opcional. No entanto, se a instância referenciada for desalocada e você tentar acessá-la, seu aplicativo irá travar.
Aqui está um exemplo de referências não próprias:
class Pais {
let nome: String
var cidadeCapital: Cidade
init(nome: String, nomeCidadeCapital: String) {
self.nome = nome
self.cidadeCapital = Cidade(nome: nomeCidadeCapital, pais: self)
}
deinit {
print("\(nome) foi desalocado.")
}
}
class Cidade {
let nome: String
unowned var pais: Pais // Assume que Cidade sempre terá uma referência válida para Pais
init(nome: String, pais: Pais) {
self.nome = nome
self.pais = pais
}
deinit {
print("\(nome) foi desalocado.")
}
}
var francia: Pais? = Pais(nome: "França", nomeCidadeCapital: "Paris")
print(francia?.cidadeCapital.nome ?? "Sem capital")
// Saída: Paris
francia = nil
// Saída: França foi desalocado.
// Saída: Paris foi desalocado.
Neste exemplo, Cidade
mantém uma referência não própria para Pais
, indicando que Cidade
espera que Pais
exista enquanto Cidade
existir.
Conclusão
Entender referências fortes, fracas e não próprias é crucial para um gerenciamento eficaz de memória em Swift. Referências fortes garantem que os objetos permaneçam na memória, enquanto referências fracas e não próprias ajudam a evitar ciclos de retenção e vazamentos de memória. Ao usar esses conceitos de maneira judiciosa, você pode escrever código Swift mais limpo e eficiente.