从Swift 5开始,引入了新的案例属性@unknown
。
使用@unknown
而不使用@unknown
的确切区别是什么?在这种情况下,我们必须使用CallbackPath
关键字吗?
答案 0 :(得分:4)
每个switch语句必须是详尽的。也就是说,所考虑类型的每个可能值都必须与切换条件之一匹配。如果不适合为每个可能的值都提供一个大小写,则可以定义一个默认大小写,以涵盖未明确寻址的所有值。该默认情况由default关键字指示,并且必须始终显示在最后。
例如:
let someCharacter: Character = "z"
switch someCharacter {
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
switch语句的第一个字母与英文字母的第一个字母a匹配,第二个字母与最后一个字母z匹配。由于switch必须为每个可能的字符而不是每个字母字符都有一个大小写,因此该switch语句使用默认大小写来匹配a和z以外的所有字符。此规定可确保switch语句是详尽无遗的
来自Reinder's blog post on "What's New In Swift 5.0":
在Swift 5.0中,可以将新的
@unknown
关键字添加到default
开关盒。这不会改变default
的行为,因此 该案例仍将与其余案例中未处理的所有案例匹配switch
块。switch fruit { case .apple: ... @unknown default: print("We don't sell that kind of fruit here.") }
如果您使用
@unknown
关键字,则会在Xcode中触发警告 处理可能不详尽的switch
语句,因为 枚举更改。您可以故意考虑这种新情况, 感谢警告,而仅仅default
是不可能的。好处是,由于
default
的工作方式,您的代码不会 如果将新案例添加到枚举,则中断–但是您会被警告。 干净!
更多参考:Hacking with Swift
答案 1 :(得分:4)
来自SE-0192: Handling Future Enum Cases(重点是我):
切换非冻结的
enum
时,switch
声明 与之匹配的匹配项必须包含一个全称(通常为default
或 一种“忽略”_
模式)。switch excuse { case .eatenByPet: // … case .thoughtItWasDueNextWeek: // … }
否则将在Swift 5中产生警告。 如果实际遇到未知枚举,则在运行时陷阱。
枚举的所有其他用途(
if case
,创建,访问成员等) 不要换。仅对开关进行详尽检查 受冻结/非冻结区分的影响。非穷举开关 超过冻结枚举(和布尔值)将继续无效 所有语言模式。这是一个更复杂的示例:
switch (excuse, notifiedTeacherBeforeDeadline) { case (.eatenByPet, true): // … case (.thoughtItWasDueNextWeek, true): // … case (_, false): // … }
此开关可处理所有已知模式,但仍无法解决 当第二个元组元素是
true
。像第一个一样,这会在Swift 5中导致警告 例子。
@unknown
使用默认情况的缺点是编译器不能 不再提醒开发人员特定的枚举包含 没有在开关中明确处理。为了解决这个问题,
switch
案例将获得一个新属性@unknown
。switch excuse { case .eatenByPet: // … case .thoughtItWasDueNextWeek: // … @unknown default: // … }
与常规默认值类似,
@unknown
默认值可匹配任何值;它是 “包罗万象”的案例。但是,如果出现以下情况,编译器将产生警告 枚举的所有已知元素尚未匹配。这是 警告而不是错误,以便向枚举添加新元素 仍然是与源兼容的更改。 (这也是为什么@unknown默认 匹配任何值,而不只是匹配在编译时未看到的值。)
@unknown
只能应用于默认值或包含 单一模式_。即使在后一种情况下,也必须使用@unknown
最后一种情况在开关中。此限制将进一步讨论 在“未来方向”下的“未知模式”部分中。如果模式中的所有枚举都被匹配,编译器将发出警告 @unknown被显式注释为冻结,或者没有枚举 在所有模式中。这是警告而非错误,因此 将枚举注释为冻结将仍然是与源兼容的更改。如果 模式包含隐式冻结的所有枚举(即 (因为它是用户定义的Swift枚举),因此允许@unknown 以便更轻松地适应新添加的案例。
@unknown
有一个缺点,那就是不可测试,因为存在 无法创建与任何已知情况都不匹配的enum
值, 如果有的话,没有一种安全的使用方式。然而, 将@unknown
与其他情况下使用fallthrough结合起来可以得到 在仍然遵循另一个案例的行为的情况下 新情况下的编译器警告。switch excuse { case .eatenByPet: showCutePicturesOfPet() case .thoughtItWasDueNextWeek: fallthrough @unknown default: askForDueDateExtension() }
答案 2 :(得分:1)
在仅使用default
的情况下,它用作switch
与任何选项都不匹配的情况。让我们来看第一个详尽的案例:
enum Option {
case A
case B
}
func optionSelected(option: Option) {
switch(option) {
case .A:
print("You chose A!")
case .B:
print("You chose B!")
}
}
此示例是详尽无遗的,我们不会出现任何错误。但是,如果我们需要在enum
中添加选项怎么办?
enum Option {
case A
case B
case C
}
func optionSelected(option: Option) {
switch(option) {
case .A:
print("You chose A!")
case .B:
print("You chose B!")
}
}
在第二个示例中,我们将收到错误Switch must be exhaustive
。为避免此错误,我们可以实现默认情况:
enum Option {
case A
case B
case C
}
func optionSelected(option: Option) {
switch(option) {
case .A:
print("You chose A!")
case .B:
print("You chose B!")
default:
print("You chose other option!")
}
}
如果用户选择了选项C,则他将进入默认情况。但是,当我们在枚举中添加选项D,E等时,会发生什么?如果我们不更改switch
,它们都会落入default
中。这可能不是问题,取决于您要实现的目标。
现在,使用@unknown
,我们继续捕获所有其他选项,但是这里的区别在于,如果编译器的所有已知元素,我们都会发出警告Switch must be exhaustive
(不是错误!)。枚举尚未匹配(即,开关未详尽)。
enum Option2 {
case A
case B
case C
}
func optionSelected2(option: Option2) {
switch(option) {
case .A:
print("You chose A!")
case .B:
print("You chose B!")
case .C:
print("You chose C!")
@unknown default:
print("You chose other option!")
}
}
如果我们添加选项D,E等,我们只会看到警告,然后决定是否要实现其他情况(例如,我们希望为选项D和E提供自定义消息),或者是否只需保留默认消息“您选择了另一个选项”即可。认为它是一个友好的余数,而不是一个大的红色错误:)
其他示例:https://www.raywenderlich.com/55728-what-s-new-in-swift-5
答案 3 :(得分:1)
暗示您将永远警告您您的枚举的答案是错误的。这是关于Swift如何处理外部库/框架中的C(和Objective-C)枚举的方法。 Swift很少的标准库枚举会受到影响。
好的,让我们考虑一个实际的例子。我们针对可可粉列举了一个详尽的开关:
var err : [URLError.NetworkUnavailableReason] = ...
switch err {
case URLError.NetworkUnavailableReason.cellular: break
case URLError.NetworkUnavailableReason.expensive: break
case URLError.NetworkUnavailableReason.constrained: break
}
这时我们得到警告。为什么?
好吧,我们的开关现在是详尽无遗的,但它可能总是并非详尽无遗。如果框架以后增加了案例怎么办?我们的编译后的代码不会更改,因此当新案例进入交换机时,它将崩溃(陷阱)。
因此,我们需要一种方法,即使框架发生更改,也允许我们的代码继续工作。因此,编译器告诉我们:“即使开关是详尽无遗的,也要添加默认大小写。”
现在,当然可以添加一个普通默认案例:
switch err {
case URLError.NetworkUnavailableReason.cellular: break
case URLError.NetworkUnavailableReason.expensive: break
case URLError.NetworkUnavailableReason.constrained: break
default: break
}
问题在于,如果框架更改,我们将永远不会听到。因此,有一种更好的方法,@unknown default
:
switch err {
case URLError.NetworkUnavailableReason.cellular: break
case URLError.NetworkUnavailableReason.expensive: break
case URLError.NetworkUnavailableReason.constrained: break
@unknown default: break
}
这意味着:“嘿,编译器,我不希望有更多的情况,但是,如果我尝试针对该框架编译该项目,而您发现还有另一种情况,请警告我< / em>,以便可以将其显式添加到我的交换机。”
这就是@unknown
的特别之处。如果在我们背后增加了另一种情况,则编译器将向我们给出另一个警告,告诉我们有关该问题的信息,我们可以修复代码以包含它。换句话说,您服从警告 now 来摆脱警告 now ,以换取将来可能有用的警告 。 >
关于此语法的另一点好处是,如果我们向不是详尽的 now 的开关添加@unknown default
,则编译器将警告我们< em>那个。