152 votes

Comment utiliser Swift @autoclosure

J'ai remarqué en écrivant un assert dans Swift que la première valeur est typée en tant que

@autoclosure() -> Bool

avec une méthode surchargée pour renvoyer une méthode générique T pour en tester l'existence via la valeur LogicValue protocol .

Cependant, si l'on s'en tient strictement à la question qui nous occupe. Il semble vouloir un @autoclosure qui renvoie un Bool .

L'écriture d'une fermeture réelle qui ne prend pas de paramètres et renvoie un Bool ne fonctionne pas, elle veut que j'appelle la fermeture pour la faire compiler, comme ceci :

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)

Cependant, le simple fait de passer un Bool fonctionne :

assert(false, "No user has been set", file: __FILE__, line: __LINE__)

Alors, qu'est-ce qui se passe ? Qu'est-ce que @autoclosure ?

Editar: @auto_closure a été renommé @autoclosure

277voto

eddie_c Points 749

Considérons une fonction qui prend un argument, une fermeture simple qui ne prend aucun argument :

func f(pred: () -> Bool) {
    if pred() {
        print("It's true")
    }
}

Pour appeler cette fonction, nous devons passer dans une fermeture

f(pred: {2 > 1})
// "It's true"

Si nous omettons les accolades, nous passons dans une expression et c'est une erreur :

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosure crée une fermeture automatique autour de l'expression. Ainsi, lorsque l'appelant écrit une expression comme 2 > 1 il est automatiquement intégré dans une fermeture pour devenir {2 > 1} avant qu'il ne soit transmis à f . Donc si nous appliquons ceci à la fonction f :

func f(pred: @autoclosure () -> Bool) {
    if pred() {
        print("It's true")
    }
}

f(pred: 2 > 1)
// It's true

Il fonctionne donc avec une simple expression sans qu'il soit nécessaire de l'enfermer dans une fermeture.

2 votes

En fait, le dernier ne fonctionne pas. Il devrait être f({2 >1}())

0 votes

@JoelFischer Je vois la même chose que @JackyBoy. Appel à f(2 > 1) travaux. Appeler f({2 > 1}) échoue avec error: function produces expected type 'Bool'; did you mean to call it with '()'? . Je l'ai testé dans un terrain de jeu et avec le REPL de Swift.

0 votes

Je crois avoir lu l'avant-dernière réponse comme étant la dernière réponse, je vais devoir revérifier, mais il serait logique que cela échoue, car vous mettez essentiellement une fermeture dans une fermeture, d'après ce que j'ai compris.

31voto

matt Points 60113

Voici un exemple concret : mon print (il s'agit de Swift 3) :

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator:separator, terminator: terminator)
    #endif
}

Quand vous dites print(myExpensiveFunction()) , mon print supplante celle de Swift print et s'appelle. myExpensiveFunction() est donc enveloppée dans une fermeture et non évalué . Si nous sommes en mode "Release", il sera jamais être évaluée, car item() ne sera pas appelé. Ainsi, nous avons une version de print qui n'évalue pas ses arguments en mode Release.

11voto

connor Points 9825

Description de auto_closure à partir des docs :

Vous pouvez appliquer l'attribut auto_closure à un type de fonction qui a un attribut type de paramètre () et qui renvoie le type d'une expression (cf. Attributs de type). Une fonction d'autoclose capture une fermeture implicite sur l'expression spécifiée, au lieu de l'expression elle-même. L'exemple suivant l'exemple suivant utilise l'attribut auto_closure pour définir un fonction assert très simple :

Et voici l'exemple qu'utilise Apple pour l'accompagner.

func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
    if !condition() {
        println(message)
    }
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

En gros, cela signifie que vous passez une expression booléenne en tant que premier argument au lieu d'une fermeture et il crée automatiquement une fermeture pour vous. C'est pourquoi vous pouvez passer false dans la méthode parce que c'est une expression booléenne, mais vous ne pouvez pas passer une fermeture.

15 votes

Notez qu'il n'est pas nécessaire d'utiliser la fonction @auto_closure ici. Le code fonctionne bien sans lui : func simpleAssert(condition: Bool, message: String) { if !condition { println(message) } } . Utilisez @auto_closure lorsque vous devez évaluer un argument de manière répétée (par exemple, si vous implémentez un système de gestion de la qualité de l'eau). while -) ou vous devez retarder l'évaluation d'un argument (par exemple, si vous mettez en œuvre le court-circuitage && ).

1 votes

@nathan Salut, nathan. Pourriez-vous me citer un exemple concernant l'utilisation de autoclosure avec un while -comme une fonction ? Je n'arrive pas à comprendre. Merci beaucoup par avance.

0 votes

@connor Vous pourriez vouloir mettre à jour votre réponse pour Swift 3.

4voto

onmyway133 Points 2196

Cela montre un cas utile de @autoclosure https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/

Maintenant, l'expression conditionnelle passée comme premier paramètre de until sera automatiquement transformée en une expression de fermeture et pourra être appelée à chaque fois dans la boucle

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
    while !pred() {
        block()
    }
}

// doSomething until condition becomes true
until(condition) {
    doSomething()
}

2voto

Bob Points 81

C'est juste un moyen de se débarrasser des accolades dans un appel de fermeture, exemple simple :

    let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
    let non = nonAutoClosure( { 2 > 1} )

    let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
    var auto = autoClosure( 2 > 1 ) // notice curly braces omitted

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X