web-dev-qa-db-ja.com

Swift @autoclosureの使用方法

Swiftにassertを書くと、最初の値が

@autoclosure() -> Bool

ジェネリックT値を返すオーバーロードメソッドを使用して、LogicValueprotocolを介して存在をテストします。

しかし、手元の質問に厳密にこだわります。 Boolを返す@autoclosureが必要なようです。

パラメーターを使用せず、Boolを返す実際のクロージャーを作成しても機能しません。次のように、クロージャーを呼び出してコンパイルします。

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

ただし、Boolを渡すだけで機能します。

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

それで何が起こっているのでしょうか? @autoclosureとは何ですか?

編集:@auto_closureの名前が変更されました@autoclosure

142
Joel Fischer

引数を1つとる関数、引数をとらない単純なクロージャーを考えてみましょう。

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

この関数を呼び出すには、クロージャーを渡す必要があります

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

中括弧を省略すると、式を渡していることになり、エラーになります。

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

@autoclosureは、式の周りに自動閉鎖を作成します。したがって、呼び出し元が2 > 1のような式を記述すると、fに渡される前に、クロージャーに自動的にラップされて{2 > 1}になります。これを関数fに適用すると:

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

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

そのため、クロージャにラップする必要なく、式だけで機能します。

259
eddie_c

これが実用的な例です。私のprintオーバーライド(これはSwift 3です):

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

print(myExpensiveFunction())と言うと、私のprintオーバーライドがSwiftのprintを覆い隠して呼び出されます。したがって、myExpensiveFunction()はクロージャでラップされますおよび評価されません。リリースモードの場合は、item()が呼び出されないため、neverが評価されます。したがって、リリースモードで引数を評価しないprintのバージョンがあります。

29
matt

ドキュメントのauto_closureの説明:

Auto_closure属性は、パラメータタイプが()で、式のタイプを返す関数タイプに適用できます(タイプ属性を参照)。自動閉鎖関数は、式自体ではなく、指定された式の暗黙的な閉鎖をキャプチャします。次の例では、auto_closure属性を使用して、非常に単純なアサート関数を定義しています。

そして、これはAppleが使用する例です。

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

基本的には、クロージャーの代わりにブール式を最初の引数として渡すと、クロージャーが自動的に作成されます。ブール式であるためメソッドにfalseを渡すことができますが、クロージャーを渡すことはできません。

11
Connor

これは@autoclosurehttps://airspeedvelocity.net/2014/06/28/extending-the-Swift-language-is-cool-but-be-careful/ の便利なケースを示しています

これで、最初のパラメーターとしてuntilに渡された条件式は、クロージャー式に自動的にラップされ、ループのたびに呼び出すことができます

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

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

これは、クロージャー呼び出しで中括弧を取り除くための単なる方法です、簡単な例:

    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