Faut-il toujours utiliser weak ou unowned dans la liste de capture?
sobes.tech IA
Réponse de l'IA
Non, pas toujours.
Nous utilisons weak ou unowned dans la liste de capture pour éviter les cycles de rétention.
Un cycle de rétention se produit lorsque deux objets ou plus se référencent mutuellement de manière forte, empêchant la mémoire d'être libérée.
Il faut utiliser weak ou unowned lorsque la fermeture fait référence forte à self (ou une autre variable d'instance de classe/acteur), et que self (ou un autre objet) a une référence forte à cette fermeture.
Exemples de situations où cela est nécessaire :
- Opérations asynchrones (requêtes réseau, traitement de données)
- Pattern Observer
- Blocs d'animation
- Files d'attente de dispatch
Si la fermeture ne capture pas self (ou une autre variable de classe/acteur ayant une référence forte à la fermeture), ou capture des types structuraux/enums, l'utilisation de weak ou unowned n'est pas nécessaire.
Différences entre weak et unowned :
| Type | Description | Quand utiliser |
|---|---|---|
weak |
Référence faible (optionnelle). Peut devenir nil. |
Lorsque le cycle de vie de la fermeture est plus court ou égal à celui de l'objet capturé, et que l'objet peut devenir nil avant la fin de la fermeture. |
unowned |
Référence non optionnelle. Ne peut pas devenir nil. |
Lorsque le cycle de vie de la fermeture est plus court ou égal à celui de l'objet capturé, et que l'objet est garanti d'exister jusqu'à la fin de la fermeture. Si l'objet est libéré avant l'exécution de la fermeture, cela provoquera une erreur (référence unowned à une instance désallouée). |
Exemple d'utilisation de weak dans la liste de capture :
class MyClass {
var name: String = "Test"
func performAsyncOperation() {
// La fermeture capture `self`.
// MyClass peut être libéré avant la fin de l'opération asynchrone.
DispatchQueue.global().async { [weak self] in
guard let self = self else {
// `self` a été libéré, sortir
return
}
print("Opération terminée pour \(self.name)")
}
}
deinit {
print("MyClass désalloué")
}
}
Exemple d'utilisation de unowned dans la liste de capture :
class ViewController {
var dismissButton: UIButton!
func setupButton() {
dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
// La fermeture capture `self`.
// Supposons que `self` (ViewController) vit plus longtemps ou aussi longtemps que l'action du bouton.
// Dans ce cas, l'action cible n'est pas une fermeture, mais illustre la notion d'`unowned` dans un cycle de référence fort.
// Si c'était *une* fermeture, par exemple, dans un gestionnaire d'événements UIControl.Event,
// et que ViewController existe pendant le clic :
// dismissButton.addAction(UIAction { [unowned self] _ in
// self.dismiss(animated: true, completion: nil)
// }, for: .touchUpInside)
}
@objc func dismiss() {
// Fermer le contrôleur de vue
}
}
Si la fermeture ne crée pas de cycle de rétention, l'utilisation de weak ou unowned n'est pas obligatoire, et peut même entraîner des vérifications inutiles (dans le cas de weak) ou des erreurs potentielles (dans le cas de unowned).
Par exemple, une fermeture qui ne capture pas self ou un autre objet, qui a une référence forte que la fermeture possède :
func fetchData(completion: @escaping (Data) -> Void) {
URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
guard let data = data else { return }
// Il n'y a pas de capture de `self` ou d'autres objets pouvant causer un cycle de rétention.
completion(data)
}.resume()
}
Dans ce cas, [weak self] ou [unowned self] dans la liste de capture ne sont pas nécessaires.