如何使用Swift @autoclosure

时间:2014-06-08 03:05:30

标签: closures swift

我注意到在Swift中写assert时,第一个值被输入为

@autoclosure() -> Bool

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

然而严格坚持手头的问题。它似乎希望@autoclosure返回Bool

编写一个不带参数并返回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

6 个答案:

答案 0 :(得分:255)

考虑一个带有一个参数的函数,一个不带参数的简单闭包:

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

因此它只使用一个表达式而无需将其包装在闭包中。

答案 1 :(得分:28)

这是一个实际的例子 - 我的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,它不会在发布模式下评估其参数。

答案 2 :(得分:11)

来自文档的auto_closure说明:

  

您可以将auto_closure属性应用于具有。的函数类型   参数类型为()并返回表达式的类型(请参阅   输入属性)。 autoclosure函数捕获隐式闭包   在指定的表达式上,而不是表达式本身。该   以下示例使用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传递给方法,因为它是一个布尔表达式,但不能传递闭包。

答案 3 :(得分:4)

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

答案 4 :(得分:1)

这只是一种摆脱闭包调用中花括号的方法,简单的例子:

    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

答案 5 :(得分: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()
}