SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
16.11.2024

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.

Video

Did you like this article? Rate it from 1 to 5:

Thank you for voting!