Swift @autoclosureの使い方


148

assertSwiftでを書いているときに、最初の値が次のように入力されていることに気付きました

@autoclosure() -> Bool

T介して存在をテストするために、ジェネリック値を返すオーバーロードされたメソッドを使用しLogicValue protocolます。

しかし、目の前の質問に厳密に固執します。@autoclosureを返すが必要なようですBool

パラメータをとらずにBoolを返す実際のクロージャを書くことは機能しません、それは私がそれをコンパイルするためにクロージャを呼び出すことを望みます、それはそうです:

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

ただし、ブール値を渡すだけで機能します。

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

それで、何が起こっているのですか?なに@autoclosure

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

回答:


269

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に、自動的にクロージャにラップさ{2 > 1}fます。これを関数に適用するとf

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

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

したがって、クロージャでラップする必要なしに、式だけで動作します。


2
実際には最後のものは機能しません。それはする必要がありますf({2 >1}())
ルイペレス

@JoelFischer私は@JackyBoyと同じものを見ています。呼び出しがf(2 > 1)機能します。呼び出しはでf({2 > 1})失敗しerror: function produces expected type 'Bool'; did you mean to call it with '()'?ます。私はそれを遊び場とSwift REPLでテストしました。
Ole Begemann 2014年

私はどういうわけか最後から2番目の答えを最後の答えとして読みました、私は再確認する必要がありますが、私が理解していることから、基本的にクロージャー内にクロージャーを置いているので、失敗した場合は理にかなっています。
Joel Fischer 14年

3
彼らがその理由を書いたブログ投稿がありますdeveloper.apple.com/swift/blog/?id=4
mohamed-ted

5
素晴らしい説明。それはですので、スウィフト1.2「autoclosure」になりましパラメータ宣言の属性であることにも注意してくださいfunc f(@autoclosure pred: () -> Bool)
マサ

30

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()呼ばれることはありません。したがってprint、リリースモードでは引数を評価しないバージョンがあります。


私はパーティーに遅れましたが、評価の影響は何myExpensiveFunction()ですか。autoclosureを使用する代わりに、関数をのようprint(myExpensiveFunction)に出力する場合、どのような 影響がありますか?ありがとう。
crom87

11

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

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

そして、これはアップルが一緒に使用する例です。

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をメソッドに渡すことができるのはそのためですが、クロージャーを渡すことはできません。


15
ここでは実際に使用する必要がないことに注意してください@auto_closure。コードはそれなしで正常に動作しますfunc simpleAssert(condition: Bool, message: String) { if !condition { println(message) } }。使用@auto_closureあなたは(あなたが実装された場合には、例えば、繰り返しの引数を評価する必要があるwhile様機能)や、あなたが(例えば、あなたは短絡を実装した場合、引数の遅延評価に必要です&&)。
ネイサン2014年

1
@nathanこんにちは、nathan。のような関数の使用に関するサンプルを教えautoclosurewhileいただけますか?私はそれを理解していないようです。よろしくお願いします。
Unheilig

@connor Swift 3の回答を更新することをお勧めします
jarora

4

これはhttps://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/の有用なケースを示してい@autoclosure ます

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

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

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

2

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

    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

0

@autoclosure調理された関数(または返された型)を受け入れる関数パラメータであり、一般的なclosure関数は生の関数を受け入れます

  • @autoclosure引数の型パラメーターは '()'でなければなりません
    @autoclosure ()
  • @autoclosureは、適切な戻り型のみを持つ任意の関数を受け入れます
  • 閉鎖の結果は需要によって計算されます

例を見てみましょう

func testClosures() {

    //closures
    XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
    XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))

    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
        return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
    }))

    //@autoclosure
    XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld"))

    XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
    XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
    XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))

}

//functions block
func foo0() -> String {
    return "foo0"
}

func foo1(i1: Int) -> String {
    return "foo1 " + String(i1)
}

func foo2(i1: Int, i2: Int) -> String {
    return "foo2 " + String(i1 + i2)
}

//closures block
func fooWithClosure0(p: () -> String) -> String {
    return "fooWithClosure0 " + p()
}

func fooWithClosure1(p: (Int) -> String) -> String {
    return "fooWithClosure1 " + p(1)
}

func fooWithClosure2(p: (Int, Int) -> String) -> String {
    return "fooWithClosure2 " + p(1, 2)
}

//@autoclosure
func fooWithAutoClosure(a: @autoclosure () -> String) -> String {
    return "fooWithAutoClosure " + a()
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.