web-dev-qa-db-fra.com

Comment utiliser Swift @autoclosure

J'ai remarqué en écrivant un assert in Swift que la première valeur est tapée comme

@autoclosure() -> Bool

avec une méthode surchargée pour renvoyer une valeur générique T, pour tester l'existence via le LogicValueprotocol.

Cependant, s'en tenir strictement à la question posée. Il semble vouloir un @autoclosure qui retourne un Bool.

Écrire une fermeture réelle qui ne prend aucun paramètre et retourne un Bool ne fonctionne pas, il veut que j'appelle la fermeture pour le compiler, comme suit:

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

Cependant, passer simplement un Bool fonctionne:

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

Alors, quoi de neuf? Quel est @autoclosure?

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

142
Joel Fischer

Prenons 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 à une fermeture

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

Si nous omettons les accolades, nous passons 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 telle que 2 > 1, il est automatiquement intégré dans une fermeture pour devenir {2 > 1} avant qu'il ne soit passé à 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

Donc, cela fonctionne avec juste une expression sans avoir besoin de l'envelopper dans une fermeture.

259
eddie_c

Voici un exemple pratique - my print override (Swift 3)]:

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

Lorsque vous dites print(myExpensiveFunction()), mon print remplace celui de Swift print et est appelé. myExpensiveFunction() est donc enveloppé dans une fermeture et non évaluée. Si nous sommes en mode Release, cela sera jamais, car item() ne sera pas appelé. Nous avons donc une version de print qui n'évalue pas ses arguments en mode Release.

29
matt

Description de auto_closure à partir de la documentation:

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

Et voici l'exemple Apple utilise avec lui.

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.")

Fondamentalement, cela signifie que vous passez une expression booléenne en tant que premier argument au lieu d’une clôture et que cela crée automatiquement une fermeture pour vous. C'est pourquoi vous pouvez transmettre false dans la méthode car il s'agit d'une expression booléenne, mais vous ne pouvez pas transmettre de clôture.

11
Connor

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

Maintenant, l'expression conditionnelle passée en tant que premier paramètre à jusqu'à sera automatiquement intégrée dans une expression de fermeture et peut être appelée à chaque fois autour de la boucle.

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

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

C'est juste un moyen de se débarrasser des accolades lors d'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
1
Bobby