Swifts guard
语句非常棒。在某些情况下,我们可能希望在使用return
退出时执行一个调用。
final class AppCoordinator {
func showApplePaySplash() -> Void { /* some presentation logic */ }
}
final class OnboardingCoordinator {
init(settings: Settings, parent: AppCoordinator) {
// This code should probably use a `switch` statement and not `guard`, but I am curious about this
guard settings.hasSeenApplePaySplash else {
parent.showApplePaySplash() // method returning `Void`
return
}
// Some more logic...
}
}
我很好奇的是,是否可以缩短语法:
guard settings.hasSeenApplePaySplash else {
parent.showApplePaySplash()
return
}
由于这是在init
内,我们无法写:
guard settings.hasSeenApplePaySplash else {
return parent.showApplePaySplash() // compilation error: `'nil' is the only return value permitted in an initializer`
}
我们当然可以将这四行更改为此oneliner:
guard settings.hasSeenApplePaySplash else { parent.showApplePaySplash(); return }
其中很好地读出了恕我直言。但是我仍然希望摆脱return
(因为我很好奇,如果有可能的话。不需要告诉我:“只要使用回归男人”)。
在另一个场景中,我们希望guard
针对某些未定义的不良行为/状态:
guard index < myArray.count else { fatalError("Array out of bounds exception, did you think about X, Y, Z?") }
我们不需要编写return
,因为方法fatalError
会返回名为Never
的特定类型。
所以如果我们可以改变签名:
func showApplePaySplash() -> Void
使用Never
,如下所示:
func showApplePaySplash() -> Never
然后我们可以替换:
guard settings.hasSeenApplePaySplash else { parent.showApplePaySplash(); return }
只有:
guard settings.hasSeenApplePaySplash else { parent.showApplePaySplash() }
但是Never
没有任何初始值设定项。似乎创建Never
的唯一可能性是使用fatalError
之类的方法来创建崩溃。
我通过@ guy-daher找到了这个execellent SO answer - 可以替换fatalError
,从而可以在测试中“捕获”它。但它使用waitForExpectations(timeout: 0.1)
,这在测试套件之外是不可能的?
所以Never
可能在这里没有帮助。 Pre Swift 4(前Swift 3?)有一个名为@noreturn
的函数注释,看起来它可能有帮助吗?
有没有办法实现这个目标? :)
添加免责声明这是一个坏主意,请不要继续用好奇心驱使我的问题。
答案 0 :(得分:2)
为什么不使用defer来指定退出清理代码?
final class OnboardingCoordinator {
init(settings: Settings, parent: AppCoordinator) {
defer {
parent.showApplePaySplash() // method returning `Void`
}
guard settings.hasSeenApplePaySplash else {
return
}
// Some more logic...
}
}
答案 1 :(得分:2)
Never
is the new @noreturn
和@noreturn
意味着在函数返回后,字面上的执行不可能继续。 Never
的重点恰恰在于它是一种无人居住的类型,并且无法创建它的实例。
Never
(以及它之前的@noreturn
)对编译器有特殊意义:当你调用一个“永不返回”的函数时,编译器不必假设有一个有效的函数函数调用后的代码路径,并且可以执行优化,假设代码永远不会被执行。在实践中,LLVM在调用之后添加陷阱指令(如{8}上的ud2
),以确保程序在函数实际返回时崩溃。
您可以执行以下操作之一:
init?
更改为init(...) throws
,使parent.showApplePaySplash()
返回Error
,并在throw parent.showApplePaySplash()
子句中使用guard
; init
特殊的情况下让您感到安宁,并返回nil
; init
变为私有,并使用class func
来创建您的对象(这是最好的样式恕我直言,因为我的理念是初始化程序通常应该只确保对象是自洽的,并且应该在另一个层面确保与其他一些国家的一致性。)