Cycle de référence dans Swift

Mar 14 2023
Même certains développeurs expérimentés font de telles erreurs
La gestion de la mémoire pour un développeur n'est généralement pas triviale. Grâce au comptage automatique des références (ARC), la gestion de la mémoire est simplifiée pour les développeurs iOS.
Diagramme par auteur, cycle de référence entre deux et plus de deux objets

La gestion de la mémoire pour un développeur n'est généralement pas triviale. Grâce au comptage automatique des références (ARC), la gestion de la mémoire est simplifiée pour les développeurs iOS. Cela fonctionne juste la plupart du temps. Cependant, le cycle de référence est très délicat et j'ai vu de nombreux développeurs expérimentés faire de telles erreurs.

La documentation officielle parle principalement du cycle de référence de base entre deux objets A<->B. Ce que je vais montrer dans cet article, c'est le cycle de référence entre 3 ou 4 objets, ce qui est plus difficile à repérer.

L'application avec fuite de mémoire

L'application simple pour la démo par l'auteur

La démo est un projet iOS très simple avec deux écrans. Dans le premier écran "InitialViewController", l'utilisateur peut cliquer sur le bouton pour lancer le deuxième écran "SampleViewController", puis l'utilisateur peut cliquer sur le bouton de fermeture pour fermer le deuxième écran. Le deuxième écran est codé à l'aide de l'architecture propre VIP.

Alors que l'utilisateur aime utiliser l'application, la mémoire fuit. À partir de l'écran suivant de la fonction "Debug Memory Graph" de Xcode, nous pouvons voir que 5 instances de chacun de "SampleViewController", "SamplePresenter", "SampleInteractor" et "SampleRouter" avaient fui. Lorsque trop de fuites de mémoire, l'application sera tuée par le système, ce qui sera très embarrassant pour le développeur de l'application.

Capture d'écran par l'auteur de Xcode, Debug Memory Graph

Cycle de référence dans plusieurs objets

Examinons attentivement le code source ci-dessous pour découvrir pourquoi la mémoire fuit.

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
}

Diagramme créé par l'auteur, relation de référence entre les objets

Il y a en fait deux cycles de référence dedans :

  1. Interacteur-> Présentateur-> ViewController-> Interacteur
  2. Interacteur-> Présentateur-> Routeur -> ViewController-> Interacteur

Pour le résoudre, nous pouvons utiliser un référencement faible comme le changement ci-dessous.

Capture d'écran par auteur, comment fixer le cycle de référence pour VIP

Ensuite, nous n'avons plus de cycle de référence pour VIP selon le schéma suivant.

Diagramme créé par l'auteur, relation de référence entre les objets après la fixation

Cycle de référence lié à la capture de blocs

Ho! Cela fait du bien car nous venons de fixer deux cycles de référence, mais nous n'avons pas encore terminé. Si vous exécutez l'application maintenant, vous verrez que nous avons toujours des fuites de mémoire. La cause est la partie suivante du code.

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()
        }
        // ... 
    }
}

Diagramme créé par l'auteur, relation de référence entre les objets et le bloc

Une fois que nous savons où se situe le problème, la résolution est simple, voir ci-dessous.

Capture d'écran par auteur, comment fixer le cycle de référence pour la capture de bloc

Résumé

Pour résumer, nous avons couvert deux types de cycles de référence qui se produisent dans plusieurs objets. Vous pouvez trouver le projet de démonstration ici , et la correction a été préparée dans une demande d'extraction pour votre examen facile. J'espère que cet article vous aidera à mieux comprendre le cycle de référence et à être sûr de l'éviter.