Swift 5中切换案例“ @unknown default”和“ default”之间的区别

时间:2019-03-29 06:07:12

标签: swift swift5

从Swift 5开始,引入了新的案例属性@unknown

使用@unknown而不使用@unknown的确切区别是什么?在这种情况下,我们必须使用CallbackPath关键字吗?

4 个答案:

答案 0 :(得分:4)

default case

  

每个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语句是详尽无遗的

@unknown default case

来自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>那个。