如何使用 Swift @autoclosure
Posted
技术标签:
【中文标题】如何使用 Swift @autoclosure【英文标题】:How to use 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
【问题讨论】:
【参考方案1】:@autoclosure
@autoclosure
是一个函数参数,它接受一个熟函数(或返回类型),而一般的closure
接受一个原始函数
@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 函数)或者您需要延迟评估参数(例如,如果您正在实现短路&&
) .
@nathan 嗨,内森。您能否引用一个关于使用autoclosure
和类似while
的功能的示例?我似乎不明白这一点。非常感谢。
@connor 你可能想更新你对 Swift 3 的回答。以上是关于如何使用 Swift @autoclosure的主要内容,如果未能解决你的问题,请参考以下文章
Swift3.0-closure的@autoclosure和@escaping
Swift开发第五篇——四个知识点(Struct Mutable方法&Tuple&autoclosure&Optional Chain)