Swift 5.3 - SE-0279 Multiple Tra
2020-12-16 本文已影响0人
ienos
在最初 Swift 的定义中,当方法的最后一个参数为闭包时,称该闭包为尾随闭包(trailing closure)
作为尾随闭包有相应的语法糖,如下所示, 在方法后面用 {}
括起闭包体,同时省略闭包标签
func someFunctionThatTakesAClosure(closure: ()-> Void) {}
someFunctionThatTakesAClosure() { /// trailing closure’s body here}
当使用该规则进行判断时,我们发现,如果当一个方法含有多个闭包时,会出现代码表达不清晰的情况,如下所示
// Without trailing closure:
UIView.animate(withDuration: 0.3, animations: {
self.view.alpha = 0
}, completion: { _ in
self.view.removeFromSuperview()
})
// With trailing closure
UIView.animate(withDuration: 0.3, animations: {
self.view.alpha = 0
}) { _ in
self.view.removeFromSuperview()
}
因为被省略了闭包标签,我们并不清楚闭包所代表的含义。所以在之前的版本中是禁止在多闭包参数的方法中加入该语法糖
New Proposal
在 SE-0279 中提出在多个闭包参数的方法中,添加尾随闭包的语法,正如 UIView.animate(withDuration:animations:completion:)
举例,我们更希望的尾随闭包语法如下:
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0
} completion: { _ in
self.view.removeFromSuperview()
}
所以在 SE-0279 的规则中允许在无标签的闭包后面跟着带有标签的闭包,并加入了逆向扫描的类型检查机制
一、类型检查机制
逆向扫描参数,从最后一个带标签的闭包进行标签匹配(如果闭包的标签为 “_”,标记为无标签参数)
Q: 为什么要这么定义这样的类型检查机制 ?
因为通常我们会有主闭包的概念,通常主闭包会放在前面,所以会将第一个闭包标签忽略,后面添加附加附带标签的闭包
对下面的方法进行新机制的分析:
func when<T>(
_ condition: @autoclosure () -> Bool,
then: () -> T,
`else`: () -> T
) -> T {
condition() ? then() : `else`()
}
when(2 < 3) {
print("then")
} else: {
print("else")
}
扫描顺序 else -> then,扫描到的闭包有 else 和 then, 忽略 then 闭包标签,相当于
when(2 < 3, then: { print("then") }, else: { print("else") })
再如:
func foo(a: () -> Int = { 42 }, b: Any? = nil) {}
foo {
42
}
b 匹配被省略标签的闭包,a 使用默认值,相当于
foo(b: { 42 })
二、默认参数
我们还会这样去定义主闭包和可选闭包
func resolve(
id: UUID,
action: (Object) -> Void,
completion: (() -> Void)? = nil,
onError: ((Error) -> Void)? = nil
) {
...
}
在这种情况下是存在争议的,
下面代码并不会像我们期待的那样进行类型检查
resolve(id: paulID) { paul in
// do something with object
} onError: { error in
// handle error
}
当程序员省略两个可选闭包时,便不再使用新语法
resolve(id: paulID) { paul in
// do something with object
}
在不改变源代码兼容性的情况下,目前无法使用统一一致性尾随闭包针对所有尾随闭包的情况
三、Coding Recommand
我们在编写 API 提供给其他人使用时也要考虑到当第一个尾随闭包被忽略参数名的场景,避免影响可读性