Ciclo de referência em Swift

Mar 14 2023
Mesmo alguns desenvolvedores experientes cometem tais erros
O gerenciamento de memória para um desenvolvedor geralmente não é trivial. Graças à Contagem Automática de Referência (ARC), o gerenciamento de memória é facilitado para desenvolvedores iOS.
Diagrama por autor, ciclo de referência entre dois e mais de dois objetos

O gerenciamento de memória para um desenvolvedor geralmente não é trivial. Graças à Contagem Automática de Referência (ARC), o gerenciamento de memória é facilitado para desenvolvedores iOS. Apenas funciona na maioria das vezes. No entanto, o ciclo de referência é muito complicado e já vi muitos desenvolvedores experientes cometerem esses erros.

A documentação oficial fala principalmente sobre o ciclo básico de referência entre dois objetos A<->B. O que vou mostrar neste artigo é o ciclo de referência entre 3 ou 4 objetos, o que é mais difícil de detectar.

O aplicativo com vazamento de memória

O aplicativo simples para demonstração do autor

A demonstração é um projeto iOS muito simples com duas telas. Na primeira tela “InitialViewController”, o usuário pode clicar no botão para iniciar a segunda tela “SampleViewController”, então o usuário pode clicar no botão Fechar para fechar a segunda tela. A segunda tela é codificada usando a arquitetura limpa VIP.

Enquanto o usuário gosta de usar o App, a memória está vazando para trás. Na tela a seguir do recurso “Debug Memory Graph” do Xcode, podemos ver que 5 instâncias de cada um de “SampleViewController”, “SamplePresenter”, “SampleInteractor” e “SampleRouter” vazaram. Quando muita memória vazar, o aplicativo será encerrado pelo sistema, o que será muito embaraçoso para o desenvolvedor do aplicativo.

Captura de tela por autor do Xcode, gráfico de memória de depuração

Ciclo de Referência em Vários Objetos

Vamos dar uma olhada no código-fonte abaixo para descobrir por que o vazamento de memória.

class SampleViewController: UIViewController, SampleViewControllerInterface {
    var interactor: SampleInteractorInterface?
    // omitted the rest for simplicity
}

class SampleInteractor: SampleInteractorInterface {
    var presenter: SamplePresenterInterface?
    // omitted the rest for simplicity
}

class SamplePresenter: SamplePresenterInterface {
    var viewController: SampleViewControllerInterface?
    var router: SampleRouterInterface?
    // omitted the rest for simplicity
}

class SampleRouter: SampleRouterInterface {
    var viewController: UIViewController?
    // omitted the rest for simplicity
}

Diagrama criado pelo autor, relação de referência entre objetos

Na verdade, existem dois ciclos de referência nele:

  1. Interator->Apresentador->ViewController->Interator
  2. Interator->Apresentador->Router ->ViewController->Interactor

Para resolvê-lo, podemos usar uma referência fraca como a alteração abaixo.

Captura de tela por autor, como corrigir o ciclo de referência para VIP

Então, não temos mais um ciclo de referência para VIP conforme o diagrama a seguir.

Diagrama criado pelo autor, relação de referência entre objetos após a fixação

Ciclo de Referência Relacionado à Captura de Bloco

Hoo! Isso é ótimo porque acabamos de fixar dois ciclos de referência, mas ainda não terminamos. Se você executar o aplicativo agora, verá que ainda temos vazamentos de memória. A causa é a seguinte parte do código.

class CustomButton: UIView {
    var buttonClickHander: (() -> Void)?
    // omitted the rest for simplicity
}

class SampleViewController: UIViewController, SampleViewControllerInterface {
   private lazy var customButton: CustomButton = {
        let button = CustomButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    private func setUpUI() {
        view.addSubview(customButton)
        customButton.buttonClickHander = {
            self.interactor?.customButtonClicked()
        }
        // ... 
    }
}

Diagrama criado pelo autor, relação de referência entre objetos e bloco

Uma vez que sabemos onde está o problema, corrigi-lo é simples, veja abaixo.

Captura de tela por autor, como corrigir o ciclo de referência para captura de bloco

Resumo

Resumindo, cobrimos dois tipos de ciclos de referência que ocorrem em vários objetos. Você pode encontrar o projeto de demonstração aqui , e a correção foi preparada em uma solicitação pull para sua fácil revisão. Espero que este artigo ajude você a entender melhor o ciclo de referência e a ter confiança para evitá-lo.