Swift 可选的转义闭包参数
Swift optional escaping closure parameter
鉴于:
typealias Action = () -> ()
var action: Action = { }
func doStuff(stuff: String, completion: @escaping Action) {
print(stuff)
action = completion
completion()
}
func doStuffAgain() {
print("again")
action()
}
doStuff(stuff: "do stuff") {
print("swift 3!")
}
doStuffAgain()
有什么方法可以使 completion
参数(和 action
)成为 Action?
类型并保留 @escaping
吗?
更改类型出现以下错误:
@escaping attribute only applies to function types
删除 @escaping
属性,代码编译并运行,但似乎不正确,因为 completion
闭包正在逃离函数的范围。
SR-2552 报告说 @escaping
无法识别函数类型别名。这就是错误 @escaping attribute only applies to function types
的原因。您可以通过扩展函数签名中的函数类型来解决:
typealias Action = () -> ()
var action: Action? = { }
func doStuff(stuff: String, completion: (@escaping ()->())?) {
print(stuff)
action = completion
completion?()
}
func doStuffAgain() {
print("again")
action?()
}
doStuff(stuff: "do stuff") {
print("swift 3!")
}
doStuffAgain()
编辑 1::
我实际上是在 xcode 8 beta 版本下,其中的错误 SR-2552 尚未解决。修复那个错误,引入一个新的(你面对的那个)仍然是开放的。参见 SR-2444。
@Michael Ilseman 指出的临时解决方案是从可选函数类型中删除 @escaping
属性, 使函数保持转义。
func doStuff(stuff: String, completion: Action?) {...}
编辑 2::
SR-2444 已关闭,明确指出参数位置 中的闭包不会 转义,需要用 @escaping
标记以使其转义,但可选参数 是 隐式转义,因为 ((Int)->())?
是 Optional<(Int)->()>
的同义词,可选闭包正在转义。
来自:swift-用户邮件列表
Basically, @escaping is valid only on closures in function parameter position. The noescape-by-default rule only applies to these closures at function parameter position, otherwise they are escaping. Aggregates, such as enums with associated values (e.g. Optional), tuples, structs, etc., if they have closures, follow the default rules for closures that are not at function parameter position, i.e. they are escaping.
所以可选的函数参数默认是@escaping。
@noeascape 默认只适用于函数参数。
我 运行 遇到了类似的问题,因为混合使用 @escaping
和非 @escaping
非常令人困惑,尤其是当您需要传递闭包时。
我最终通过 = { _ in }
为闭包参数分配了一个无操作默认值,我认为这更有意义:
func doStuff(stuff: String = "do stuff",
completion: @escaping (_ some: String) -> Void = { _ in }) {
completion(stuff)
}
doStuff(stuff: "bla") {
stuff in
print(stuff)
}
doStuff() {
stuff in
print(stuff)
}
我在 Swift 3 中使用它,没有任何警告,只是这样:
func doStuff(stuff: String, completion: (()->())? ) {
print(stuff)
action = completion
completion?()
}
示例中要理解的重要一点是,如果将 Action
更改为 Action?
,则闭包 是 转义。那么,让我们按照您的建议进行吧:
typealias Action = () -> ()
var action: Action? = { }
func doStuff(stuff: String, completion: Action?) {
print(stuff)
action = completion
completion?()
}
好的,现在我们调用 doStuff
:
class ViewController: UIViewController {
var prop = ""
override func viewDidLoad() {
super.viewDidLoad()
doStuff(stuff: "do stuff") {
print("swift 3!")
print(prop) // error: Reference to property 'prop' in closure
// requires explicit 'self.' to make capture semantics explicit
}
}
}
好吧,这个要求只出现在转义闭包中。所以闭包正在逃避。这就是为什么你不标记它正在转义——它已经在转义了。
鉴于:
typealias Action = () -> ()
var action: Action = { }
func doStuff(stuff: String, completion: @escaping Action) {
print(stuff)
action = completion
completion()
}
func doStuffAgain() {
print("again")
action()
}
doStuff(stuff: "do stuff") {
print("swift 3!")
}
doStuffAgain()
有什么方法可以使 completion
参数(和 action
)成为 Action?
类型并保留 @escaping
吗?
更改类型出现以下错误:
@escaping attribute only applies to function types
删除 @escaping
属性,代码编译并运行,但似乎不正确,因为 completion
闭包正在逃离函数的范围。
SR-2552 报告说 @escaping
无法识别函数类型别名。这就是错误 @escaping attribute only applies to function types
的原因。您可以通过扩展函数签名中的函数类型来解决:
typealias Action = () -> ()
var action: Action? = { }
func doStuff(stuff: String, completion: (@escaping ()->())?) {
print(stuff)
action = completion
completion?()
}
func doStuffAgain() {
print("again")
action?()
}
doStuff(stuff: "do stuff") {
print("swift 3!")
}
doStuffAgain()
编辑 1::
我实际上是在 xcode 8 beta 版本下,其中的错误 SR-2552 尚未解决。修复那个错误,引入一个新的(你面对的那个)仍然是开放的。参见 SR-2444。
@Michael Ilseman 指出的临时解决方案是从可选函数类型中删除 @escaping
属性, 使函数保持转义。
func doStuff(stuff: String, completion: Action?) {...}
编辑 2::
SR-2444 已关闭,明确指出参数位置 中的闭包不会 转义,需要用 @escaping
标记以使其转义,但可选参数 是 隐式转义,因为 ((Int)->())?
是 Optional<(Int)->()>
的同义词,可选闭包正在转义。
来自:swift-用户邮件列表
Basically, @escaping is valid only on closures in function parameter position. The noescape-by-default rule only applies to these closures at function parameter position, otherwise they are escaping. Aggregates, such as enums with associated values (e.g. Optional), tuples, structs, etc., if they have closures, follow the default rules for closures that are not at function parameter position, i.e. they are escaping.
所以可选的函数参数默认是@escaping。
@noeascape 默认只适用于函数参数。
我 运行 遇到了类似的问题,因为混合使用 @escaping
和非 @escaping
非常令人困惑,尤其是当您需要传递闭包时。
我最终通过 = { _ in }
为闭包参数分配了一个无操作默认值,我认为这更有意义:
func doStuff(stuff: String = "do stuff",
completion: @escaping (_ some: String) -> Void = { _ in }) {
completion(stuff)
}
doStuff(stuff: "bla") {
stuff in
print(stuff)
}
doStuff() {
stuff in
print(stuff)
}
我在 Swift 3 中使用它,没有任何警告,只是这样:
func doStuff(stuff: String, completion: (()->())? ) {
print(stuff)
action = completion
completion?()
}
示例中要理解的重要一点是,如果将 Action
更改为 Action?
,则闭包 是 转义。那么,让我们按照您的建议进行吧:
typealias Action = () -> ()
var action: Action? = { }
func doStuff(stuff: String, completion: Action?) {
print(stuff)
action = completion
completion?()
}
好的,现在我们调用 doStuff
:
class ViewController: UIViewController {
var prop = ""
override func viewDidLoad() {
super.viewDidLoad()
doStuff(stuff: "do stuff") {
print("swift 3!")
print(prop) // error: Reference to property 'prop' in closure
// requires explicit 'self.' to make capture semantics explicit
}
}
}
好吧,这个要求只出现在转义闭包中。所以闭包正在逃避。这就是为什么你不标记它正在转义——它已经在转义了。