何时可以省略Swift的return关键字?
从第一版Swift开始,我们就可以在单表达式闭包中省略return关键字,比如这个闭包,它试图将String值数组中的每个元素转换为等效的Int:
let strings = ["1", "2", "3"]
let ints = strings.compactMap { string in
Int(string)
}
但是,如果给定的闭包包含多个表达式,则不能省略return关键字,即使该闭包不包含任何条件或单独的代码分支。因此,传递给下面的map调用的闭包需要显式地将其最后一个表达式标记为返回值:
class GameController {
private(set) var players = [Player]()
func reviveAllPlayers() {
players = players.map { player in
var player = player
player.isActive = true
player.hitPoints = 100
return player
}
}
}
在Swift 5.1中,上述行为被扩展为包括函数和计算属性,同时保持完全相同的规则。所以现在,当编写一个只包含一个计算返回值的表达式的函数时,我们也可以省略return关键字,如下所示:
extension GameController {
func playersQualifiedForNextLevel() -> [Player] {
players.filter { player in
player.isActive && player.score > 1000
}
}
}
在上面,我们实际上省略了两个return关键字——都是函数内的顶级关键字,以及传递给filter的闭包关键字。
同样,当实现一个计算属性,只返回一个表达式的结果,然后return关键字也可以被忽略——如果我们想这样做的话,这通常会使一个完整的计算属性实现在一行代码中是可能的:
extension GameController {
var gameCanBeStarted: Bool { players.count > 1 }
}
不过,值得注意的是,所有这些特性都是可选的。如果愿意,我们可以修改到目前为止所看到的每个示例,而不是始终使用显式return关键字,这样一切将保持完全相同的工作方式。
然而,当我们开始采用SwiftUI时,可能会使上述行为稍微令人困惑。当使用闭包来构造swifitui容器体时,最初看起来我们实际上可以忽略return关键字,即使是在包含多个表达式或代码路径的闭包中,就像这样:
struct RootView: View {
@ObservedObject var loginController: LoginController
var body: some View {
NavigationView {
if let user = loginController.loggedInUser {
HomeView(user: user)
} else {
LoginView(handler: loginController.performLogin)
}
}
}
}
然而,上面的闭包实际上并没有使用多个隐式返回,而是由SwiftUI的ViewBuilder处理——这是一个函数/结果构建器,它接受我们的每个视图表达式,并将它们组合成单个返回类型。
为了说明这一点,让我们看看如果我们把上面的闭包变成一个方法会发生什么:
struct RootView: View {
@ObservedObject var loginController: LoginController
var body: some View {
NavigationView(content: makeContent)
}
private func makeContent() -> some View {
if let user = loginController.loggedInUser {
HomeView(user: user)
} else {
LoginView(handler: loginController.performLogin)
}
}
}
当尝试编译上述代码时,我们现在会得到以下构建错误:
函数声明不透明的返回类型,但没有返回 语句,从中推断底层类型。
为了解决这个问题,我们必须用与SwiftUI标记许多闭包参数相同的@ViewBuilder属性来标记新的makeContent方法——这再次使我们可以声明多个表达式而不返回任何关键字:
struct RootView: View {
@ObservedObject var loginController: LoginController
var body: some View {
NavigationView(content: makeContent)
}
@ViewBuilder private func makeContent() -> some View {
if let user = loginController.loggedInUser {
HomeView(user: user)
} else {
LoginView(handler: loginController.performLogin)
}
}
}
总结一下:
-
Swift的return关键字在所有单表达式闭包、函数和计算属性中都可以被省略。但是,如果我们愿意,我们仍然可以在这些上下文中使用显式返回。
-
每当闭包、函数或computed属性包含多个顶级表达式时,我们需要用return关键字显式地标记返回值表达式。
-
当然,实际上不返回任何东西(或者,从技术上来说,返回Void)的函数或闭包根本不需要包含任何return关键字,除非我们想手动退出该作用域——例如通过执行早期返回。
-
SwiftUI在这个上下文中有点特殊,因为它不依赖隐式返回(在大多数情况下),而是使用它的ViewBuilder将给定容器中的所有表达式组合成一个视图值。