考虑以下函数:
func whatever(foo: @autoclosure () -> Int) {
let x = foo()
print(x)
}
Run Code Online (Sandbox Code Playgroud)
当然,我们可以这样调用它:
whatever(foo: 5)
// prints: 5
Run Code Online (Sandbox Code Playgroud)
然而,提供显式的闭包参数会导致编译器抱怨:
whatever(foo: { 5 })
// Error: Function produces expected type 'Int'; did you mean to call it with '()'?
Run Code Online (Sandbox Code Playgroud)
这是有意的吗?阅读文档时,@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"
Run Code Online (Sandbox Code Playgroud)
现在假设我想对一个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>'
}
}
Run Code Online (Sandbox Code Playgroud)
澄清一下:defaultValue是类型() -> T。在我的期望中,将其提供给init(key: String, defaultValue: () -> T)应该可以工作,因为参数和参数具有相同的类型(而参数是@autoclosure)。
然而,Swift 似乎包装了提供的闭包,有效地创建了() -> () -> T,它创建了Setting<() -> T>而不是Setting<T>. 我可以通过声明 an which 采用显式非参数
来解决此问题:init@autoclosure
extension Defaults.Setting {
init(key: String, defaultValue: @escaping () -> T) {
self.init(key: key, defaultValue: defaultValue)
}
}
Run Code Online (Sandbox Code Playgroud)
真正令人畏惧的是,我可以简单地转发到init获取@autoclosure参数并且它可以工作。
我在这里遗漏了一些东西,还是 Swift 中的设计不可能为@autoclosure参数提供闭包参数?
Swift 希望您传递一个生成Intto的表达式whatever(foo:),并且 Swift 会将该表达式包装在类型为 的闭包中() -> Int。
func whatever(foo: @autoclosure () -> Int) {
let x = foo()
print(x)
}
Run Code Online (Sandbox Code Playgroud)
当你这样称呼它时:
func whatever(foo: {5})
Run Code Online (Sandbox Code Playgroud)
你传递的表达式() -> Int不是IntSwift 期望的结果。这就是为什么它建议您添加()并调用该闭包以获取返回以下值的表达式Int:
func whatever(foo: {5}())
Run Code Online (Sandbox Code Playgroud)
请注意,由于 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 }())
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)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 }())
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)closure whatever autoclosure whatever two 6 autoclosure whatever two 6
| 归档时间: |
|
| 查看次数: |
2991 次 |
| 最近记录: |