Sobes.tech
Junior

Qu'est-ce qu'une fermeture ?

sobes.tech IA

Réponse de l'IA

La fermeture (closure) en Swift est un bloc autonome de fonctionnalités ({}) qui peut être transmis et utilisé dans le code. Les fermetures peuvent capturer et stocker des références à n'importe quelle constante ou variable du contexte dans lequel elles sont définies.

Exemples d'utilisation :

  • En tant qu'arguments de fonctions (par exemple, gestionnaires de complétion).
  • Pour définir le comportement lors de l'énumération de collections (par exemple, en utilisant les méthodes map, filter, reduce, sorted).
  • Pour exécuter du code de manière différée.

Syntaxe :

// Syntaxe de base
{ (paramètres) -> typeDeRetour in
    déclarations
}

// Exemple de fermeture prenant un Int et renvoyant un String
let integerToString: (Int) -> String = { (number: Int) -> String in
    return "Numéro : \(number)"
}

Caractéristiques :

  • Capture automatique des variables.
  • Possibilité de définir des fermetures sans spécifier explicitement les types de paramètres et la valeur de retour (sucres syntaxiques).
  • Utilisation de noms abrégés pour les paramètres ($0, $1, etc.).
  • Syntaxe de fermeture avec trailing closure.

Capture de variables :

Une fermeture peut capturer des variables de son environnement. Par défaut, la capture se fait par référence. Si une fermeture est utilisée après que la variable d'origine a quitté le domaine, la fermeture conserve toujours l'accès à celle-ci.

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0 // Variable du contexte externe
    let incrementer: () -> Int = { // Fermeture
        runningTotal += amount // Capture runningTotal et amount
        return runningTotal
    }
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // Sortie : 10
print(incrementByTen()) // Sortie : 20

Listes de capture (Capture Lists) :

Utilisées pour indiquer explicitement comment les variables doivent être capturées - par valeur ou par référence, et pour éviter les cycles de références fortes (cycles de rétention).

class MyClass {
    var value = 0

    lazy var myClosure: () -> Void = { [self, value] in // Capture de self par référence, de value par valeur
        print("Valeur de self.value : \(self.value)")
        print("Valeur capturée : \(value)")
    }

    deinit {
        print("Instance de MyClass libérée")
    }
}

Pour éviter les cycles de références fortes lors de la capture d'objets de classes, utilisez la syntaxe [weak self] ou [unowned self].

class ViewController {
    var dataFetcher: DataFetcher?

    func fetchData() {
        dataFetcher?.fetch { [weak self] result in // Capture faible de self
            guard let self = self else { return } // Vérification de l'existence de self
            // Traitement du résultat
        }
    }
}

class DataFetcher {
    var completionHandler: ((Result<String, Error>) -> Void)?

    func fetch(completion: @escaping (Result<String, Error>) -> Void) {
        self.completionHandler = completion
        // Opération asynchrone...
    }
}