该特性对应的proposal是–Swift evolution-Handling Future Enum Cases

@unknown default

在Swift 4中,这样写Switch case是没有任何问题的

// UIUserInterfaceSizeClass总共就下面三个case
let axis: UIUserInterfaceSizeClass = .unspecified
switch axis {
case .unspecified:
    fallthrough
case .regular:
    fallthrough
case .compact:
    print("")
}

当升级到Swift 5后,编译器会给出一个正告

Switch covers known cases, but ‘UIUserInterfaceSizeClass’ may have additional unknown values, possibly added in future versions.

Handle unknown values using “@unknown default”

假如接受修正主张,则代码如下

let axis: UIUserInterfaceSizeClass = .unspecified
switch axis {
case .unspecified:
    fallthrough
case .regular:
    fallthrough
case .compact:
    print("")
@unknown default:
    fatalError()
}

为什么引入@unknown default

由于Swift要求switch case有必要exhausitive,为了满足该要求,在Swift 4及曾经,咱们运用switch case来进行枚举时,通常只要两种写法:

  • 枚举一切case
  • 枚举部分case,最后运用default掩盖其他case

但这两种状况都存在各自的问题:

当未来某个版本中,枚举的提供者新增了一个case,那么

  • 假如之前运用枚举时枚举了一切case,则会编译犯错,由于不满足exhausitive规矩
  • 假如运用default掩盖了一切case,虽然不会编译犯错,但由于未对新增的case进行处理,事务逻辑上或许存在潜在的危险

@unknown default就是用来解决该上面问题的,它其实就做了一件工作:

  • 当运用了@unknown default,但并没有枚举一切case时,编译器会给出未满足exhausitive的正告,而非像“default`那样编译错误

这直接带来的好处是:

  • 当运用@unknown default一起现已枚举了一切case时,当后续枚举提供者新增了case,不会由于编译犯错
  • 一起鼓励开发者运用@unknown default代替原来的default,这样后续新增或删减case时,编译器会及时提示开发者针对enum的变化做必要的调整

一句话总结该特点的意图

既做到不影响开发者源码的可编译性,又能及时提示开发者做好兼容适配工作

留意,该特点现在并不适用一切的enum,具体看下一小节

@unknown default适用范围

现在仅适用于:

  • C中的enum
  • 来自体系库入UIKitSwift Standard Library中的enum

未来或许会允许开发者自己开发的Library,但现在不适用

我列举了Enum一切或许得状况,对上述适用范围进行了验证

  • 在自己工程中界说了C Enum–ProjectSourceTestEnumType
  • 在第三方库MJRefresh(OC编写)中界说了C Enum–MJTestEnumType
  • 在SnapKit(Swift 编写)中添加了一个Swift自界说Enum–SnapKitTestEnum

func testEnumForCEnum(_ testEnum: MJTestEnumType) {
    switch testEnum { // Compiler Warning: Switch covers known cases, but 'MJTestEnumType' may have additional unknown values
    case .type1:  print("")
    case .type2: print("")
    }
}
func testEnumForStandardLibrary(_ axis: UIUserInterfaceSizeClass) {
    switch axis { // Compilier Warning: Switch covers known cases, but 'UIUserInterfaceSizeClass' may have additional unknown values, possibly added in future versions
    case .unspecified: print("")
    case .regular: print("")
    case .compact: print("")
    }
}
func testEnumForProjectCEnum(_ testEnum: ProjectSourceTestEnumType) {
    switch testEnum { // Compiler Warning: Switch covers known cases, but 'ProjectSourceTestEnumType' may have additional unknown values
    case .type1: print("")
    case .type2: print("")
    }
}
func testEnumForThirdLibrary(_ testEnum: SnapKitTestEnum) {
    switch testEnum { // 无正告
    case .case1: print("")
    case .case2: print("")
    }
}

为什么限定适用范围呢

来自官方的解释是

该特性首要适用对象并非enum in project source code,而是project所依赖的enum in library code。说白了就是给代码库开发人员的

  • 之所以运用C Enum,是由于C Enum处理起来有点复杂,没办法区别一个C Enum归于project code仍是library code,所以进行了一致处理

什么是Frozen enum

到此还没有完毕,试想一下,假如一切iOS的体系库中的枚举都运用如上规矩,那是不是就相当于主张iOS开发者在后续一切进行枚举时都添加@unknown default

似乎不太合理。所以引入了Frozen or non Frozen enum的概念,Frozen enum的写法如下所示

@frozen public enum SnapKitTestFrozenEnum {
    case case1
    case case2
}
typedef NS_CLOSED_ENUM(NSUInteger, MJTestClosedEnumType) {
    MJTestClosedEnumType1,
    MJTestClosedEnumType2
};
  • 此时再去枚举上面enum的时分,及时不写@unknown default,也不会有正告了
  • 一起,当library的开发者假如真的往frozen enum中添加了一个case,咱们接入后编译器会再次报错,奉告违反了exhausitive规矩

frozen是冻住的意思,表明不会再改动,比如体系库中的NSComparisonResult ,比较成果只要大、小和持平三种状况,打死也不会出现第三种了,这种就可以标记为frozen了

typedef NS_CLOSED_ENUM(NSInteger, NSComparisonResult) {
    NSOrderedAscending = -1L,
    NSOrderedSame,
    NSOrderedDescending
};

当然,现在大部分枚举还都是non frozen的

总结

留意@unknown default的适用范围:

一切代码中的C Enum和体系库代码中的Swift或OC编写的enum

参考

  • Swift 5 Frozen enums