Dans Swift, les closures peuvent « capturer » des valeurs et des références du contexte environnant. Si la valeur capturée est un type valeur (struct, enum), la closure copie les données. Si une référence est capturée (par exemple, une classe), la closure stocke une référence à l'instance de la classe, ce qui peut entraîner un cycle de rétention si [weak self] ou [unowned self] n'est pas utilisé.
Le mécanisme de capture permet de réaliser des tâches asynchrones et des callbacks, mais une mauvaise utilisation empêche une gestion efficace de la mémoire.
Exemple:
class MyClass { var value = 10 func doSomething() { let closure = { [weak self] in print(self?.value ?? "nil") } closure() } }
Dans cet exemple, [weak self] est utilisé pour éviter un cycle de références fortes, car un travail asynchrone est en cours.
Question: "L'utilisation de [weak self] garantit-elle toujours l'absence de fuites de mémoire lors de la capture de self dans une closure ?"
Réponse: Non, si une référence forte apparaît à l'intérieur de la closure (par exemple, via des variables intermédiaires), même avec [weak self], on peut obtenir un cycle de rétention. Par exemple:
let closure = { [weak self] in let strongSelf = self strongSelf?.doSomething() }
Si la closure est liée à un objet à longue durée de vie (par exemple, NotificationCenter), on peut obtenir un cycle de rétention si d'autres références existent pour self.
Histoire 1
Dans un projet d'application iOS, le développeur a oublié d'indiquer
[weak self]lors de la transmission de la closure dans une requête réseau. En conséquence, tant que la requête était en cours, l'objet du contrôleur de vue ne s'est pas libéré, même si l'utilisateur avait fermé l'écran. Cela a conduit à des fuites de mémoire lors de l'utilisation intensive des opérations réseau.
Histoire 2
Dans un système complexe d'abonnement à un minuteur, l'objet qui s'est abonné via une closure sans utiliser
weaka continué à exécuter ses actions même après la suppression de l'écran de la mémoire. Cela a conduit à des appels répétés d'éléments de l'UI qui n'existaient plus dans la hiérarchie, provoquant des plantages de l'application.
Histoire 3
Lors de la mise en œuvre du caching de données, la closure a capturé une référence à self sans annotation
[unowned self]dans un objet de cache à longue durée de vie. Après la destruction de l'objet d'origine, tenter d'accéder à self depuis la closure entraînait un accès à de la mémoire déjà libérée, causant un plantage de l'application.