考虑以下函数:
func whatever(foo: @autoclosure () -> Int) {
let x = foo()
print(x)
}
当然,我们可以像这样调用它:
whatever(foo: 5)
// prints: 5
但是,提供显式闭包参数会导致编译器抱怨:
whatever(foo: { 5 })
// Error: Function produces expected type 'Int'; did you mean to call it with '()'?
这是有意为之吗?阅读@autoclosure
的文档时,我没有找到关于参数是否总是被包装的声明,即使在提供闭包时也是如此。我对@autoclosure
的理解是:
采取闭包论证。如果参数不是闭包,但与闭包返回的类型相同,请包装它。
然而,我看到的行为是:无论如何都要包装论点。
一个更详细的例子让我看起来很奇怪:
struct Defaults {
static var dispatcher: Defaults = ...
subscript<T>(setting: Setting<T>) -> T { ... }
struct Setting<T> {
let key: String
let defaultValue: () -> T
init(key: String, defaultValue: @escaping @autoclosure () -> T) {
self.key = key
self.defaultValue = defaultValue
}
}
}
extension Defaults.Setting {
static var nickname: Defaults.Setting<String> {
return Defaults.Setting(key: "__nickname", defaultValue: "Angela Merkel")
}
}
// Usage:
Defaults.dispatcher[.nickname] = "Emmanuel Macron"
现在假设我想对Setting
值的键进行哈希处理:
extension Defaults.Setting {
var withHashedKey: Defaults.Setting<T> {
return Defaults.Setting(key: key.md5(), defaultValue: defaultValue)
// Error: Cannot convert return expression of type 'Defaults.Setting<() -> T>' to return type 'Defaults.Setting<T>'
}
}
澄清一下:defaultValue
属于() -> T
型。将其提供给init(key: String, defaultValue: () -> T)
,在我的期望中应该可以正常工作,因为参数和参数具有相同的类型(而参数@autoclosure
)。
然而,Swift 似乎包装了提供的闭包,有效地创建了() -> () -> T
,从而创建了Setting<() -> T>
而不是Setting<T>
。
我可以通过声明一个采用显式非@autoclosure
参数的init
来解决此问题:
extension Defaults.Setting {
init(key: String, defaultValue: @escaping () -> T) {
self.init(key: key, defaultValue: defaultValue)
}
}
真正令人生畏的是,我可以简单地转发到init
获取@autoclosure
参数并且它可以工作。
我在这里错过了一些东西,还是 Swift 的设计无法为@autoclosure
参数提供闭包参数?
Swift希望你传递一个表达式,导致Int
whatever(foo:)
,Swift 会将该表达式包装在类型() -> Int
的闭包中。
func whatever(foo: @autoclosure () -> Int) {
let x = foo()
print(x)
}
当你这样称呼它时:
func whatever(foo: {5})
您传递的表达式会导致() -> Int
,而不是 Swift 期望Int
。 这就是为什么它建议你添加()
并调用该闭包来获取返回Int
的表达式:
func whatever(foo: {5}())
请注意,由于 Swift 将{5}()
包装在闭包中,因此在调用whatever(foo:)
之前不会对其进行评估,但实际上调用会延迟到您评估let x = foo()
之前。
您可以通过在 Playground 中运行以下命令来验证这一点:
func whatever(foo: @autoclosure () -> Int) {
print("inside whatever")
let x = foo()
print(x)
let y = foo()
print(y)
}
whatever(foo: { print("hi"); return 3 }())
输出:
inside whatever hi 3 hi 3
如果您希望whatever(foo:
也能够采用() -> Int
关闭,请重载它并在调用foo
后调用自动关闭版本:
func whatever(foo: @autoclosure () -> Int) {
print("autoclosure whatever")
let x = foo()
print(x)
}
func whatever(foo: () -> Int) {
print("closure whatever")
whatever(foo: foo())
}
whatever(foo: { print("two"); return 6 })
whatever(foo: { print("two"); return 6 }())
输出:
closure whatever autoclosure whatever two 6 autoclosure whatever two 6