【问题标题】:How to use Swift @autoclosure如何使用 Swift @autoclosure
【发布时间】:2014-07-28 22:49:15
【问题描述】:

我注意到在 Swift 中编写 assert 时,第一个值的类型为

@autoclosure() -> Bool

使用重载方法返回泛型T 值,以通过LogicValue protocol 测试存在。

但严格按照手头的问题。它似乎想要一个返回 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

【问题讨论】:

    标签: closures swift


    【解决方案1】:

    @autoclosure

    @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()
    }
    

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案3】:

        考虑一个接受一个参数的函数,一个不接受任何参数的简单闭包:

        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
        

        所以它只使用一个表达式而不需要将它包装在一个闭包中。

        【讨论】:

        • 其实最后一个,不行。应该是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 对其进行了测试。
        • 我以某种方式读到倒数第二个答案作为最后一个答案,我必须仔细检查,但如果它失败了,这将是有道理的,因为你基本上是把一个闭包放在一个闭包中,从我的理解。
        • 有一篇关于他们这样做的原因的博客文章developer.apple.com/swift/blog/?id=4
        • 很好的解释。另请注意,在 Swift 1.2 中,“自动关闭”现在是参数声明的一个属性,所以它是 func f(@autoclosure pred: () -> Bool)
        【解决方案4】:

        这是一个实际示例——我的 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()有什么影响?。如果不使用自动关闭,而是将函数传递给像print(myExpensiveFunction) 这样打印,会有什么影响?谢谢。
        【解决方案5】:

        这显示了@autoclosure https://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()
        }
        

        【讨论】:

          【解决方案6】:

          文档中对 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 传递给方法,因为它是一个布尔表达式,但不能传递闭包。

          【讨论】:

          • 请注意,您实际上不需要在这里使用@auto_closure。没有它,代码可以正常工作:func simpleAssert(condition: Bool, message: String) { if !condition { println(message) } }。当您需要重复评估参数时使用@auto_closure(例如,如果您正在实现while-like 函数)或者您需要延迟评估参数(例如,如果您正在实现短路&amp;&amp;) .
          • @nathan 嗨,内森。您能否引用一个关于使用autoclosure 和类似while 的功能的示例?我似乎不明白这一点。非常感谢。
          • @connor 你可能想更新你对 Swift 3 的回答。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-29
          • 2016-05-26
          • 2014-08-19
          • 1970-01-01
          • 1970-01-01
          • 2014-08-28
          相关资源
          最近更新 更多