如何使用Swift @autoclosure


148

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

@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

Answers:


269

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

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}())
Rui Peres 2014年

@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年

我以某种方式将倒数第二个答案读作最后一个答案,我将不得不仔细检查,但是如果失败,这是有道理的,因为据我所知,您基本上是将闭包放入闭包中。
乔尔·菲舍尔

3
有关于他们这样做的原因博客帖子developer.apple.com/swift/blog/?id=4
穆罕默德-特德

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()?如果您不使用自动关闭功能而是通过将函数打印为like print(myExpensiveFunction),将会产生什么影响?谢谢。
crom87

11

来自文档的auto_closure的描述:

您可以将auto_closure属性应用于参数类型为()并返回表达式类型的函数类型(请参见类型属性)。自动闭合函数将捕获指定表达式的隐式闭合,而不是表达式本身。下面的示例在定义一个非常简单的assert函数时使用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嗨,内森。能否请您举一个有关使用autoclosure类似while函数的示例?我似乎不知道。首先十分感谢。
Unheilig 2015年

@connor您可能需要更新您的斯威夫特3.答案
jarora


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()
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.