Les fermetures en Swift

Qu'est-ce qu'une fermeture ?

Les fermetures en Swift représentent des blocs de code pouvant être transmis et réutilisés dans le même script. Ces blocs ont la capacité de conserver et de référencer des variables et des constantes, un phénomène connu sous le nom de capture de valeurs. La gestion de la mémoire des fermetures est complètement gérée par Swift, ce qui simplifie leur utilisation.

Utilisation des fermetures

Les fermetures possèdent une structure semblable à celle des fonctions. Par exemple, vous pouvez assigner une fermeture à une variable nommée bonjour qui affiche "Bonjour le monde" :

let bonjour = { print("Bonjour le monde !") }
bonjour()

Dans cet exemple, le code entre accolades, { }, représente la fermeture, qui est appelée en utilisant des parenthèses, ce qui exécute le code à l'intérieur.

Paramètres des fermetures

Tout comme les fonctions, les fermetures peuvent prendre des paramètres. En modifiant l'exemple précédent, vous pouvez dire "Bonjour" à une personne spécifique :

let bonjourNom: (String) -> () = { nom in  print("Bonjour \(nom) !")}
bonjourNom("Monde")

Ce code produit "Bonjour Monde !". Les deux éléments clés de cette fermeture sont le type (String) -> (), qui décrit le type d’entrée et de sortie, et l’expression { nom in ... }, où in sépare les paramètres du corps de la fermeture.

Inférence de types

Swift est capable de deviner le type des paramètres et du retour. Prenons un exemple où nous trions un tableau de nombres en ordre croissant.

let nombres = [38, 45, 66, 72, 19, 73]
var nombresTries = nombres.sorted(by: { (num1: Int, num2: Int) -> Bool in return num1 < num2 })

Ici, une fermeture est utilisée pour spécifier l'ordre de tri. Swift déduit le type des paramètres grâce au contexte, permettant d’écrire la fermeture sans spécifier les types, comme suit :

nombresTries = nombres.sorted(by: { num1, num2 in return num1 < num2 })

Fermetures à expression unique

Lorsqu'une fermeture consiste en une seule expression, Swift peut omettre le mot-clé return. L'expression ci-dessus peut donc être simplifiée :

nombresTries = nombres.sorted(by: { num1, num2 in num1 < num2 })

Arguments en raccourci

Swift fournit aussi des noms d'arguments en raccourci pour les fermetures en ligne. Par exemple, pour les fermetures dont l'on utilise le premier et le second argument, vous pouvez écrire :

nombresTries = nombres.sorted(by: { $0 < $1 })

Autoclosures

Les autoclosures enveloppent une expression de fermeture transmise en tant qu'argument d'une fonction, ce qui rend le code plus propre.

func rechercher(condition: () -> Bool) {  
    if condition() {  
        print("Le résultat était vrai !")  
    } else {  
        print("Le résultat était faux !")  
    }
}
rechercher(condition: { 122 < 82 }) // Output: Le résultat était faux !

Avec une autoclosure, cela devient :

func rechercher(condition: @autoclosure () -> Bool) { 
    if condition() { 
        print("Le résultat était vrai !") 
    } else { 
        print("Le résultat était faux !") 
    }
}
rechercher(condition: 122 < 82)

Fermetures à la traîne

Lorsque vous utilisez une fermeture comme dernier argument d'une fonction, la syntaxe de fermeture à la traîne vous permet de la définir en dehors de l'appel de la fonction :

func maFonction(fermeture: () -> Void) {  
    print("Dans l'appel de fonction");  
    fermeture();  
    print("Après l'appel de fermeture")
}
maFonction { print("Dans la fermeture"); }

Capture de valeurs

Les fermetures peuvent capturer les valeurs du contexte environnant. Par exemple, un compteur qui incrémente un nombre à chaque fois qu'il est appelé :

func compteur() -> () -> Int {  
    var count = 0  
    let ajouter = { () -> Int in      
        count += 1      
        return count  
    }  
    return ajouter
}
let compte = compteur()  
print(compte()) // Output: 1
print(compte()) // Output: 2

Pourquoi utiliser les fermetures ?

Les fermetures rendent le code plus lisible et concis, tout en préservant son intention d'origine. Elles permettent de simplifier le code et offrent une plus grande flexibilité en permettant de passer du code fonctionnel dans des variables et constantes.