ProgrammationDéveloppeur iOS

Comment fonctionne le mécanisme de capture de valeur et de référence dans les closures Swift ? Quelles dangers potentiels peuvent survenir lors de la capture, et comment les éviter ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

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 piégeuse.

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.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


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 weak a 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.