- 原文地址:Swift 5 Frozen enums
- 原文作者:Keith Harrison
- 译文出自:掘金翻译计划
- 本文永久链接:github.com/xitu/gold-m…
- 译者:iWeslie
- 校对者:Lobster-King
你是否已经将你的 Xcode 工程升级到了兼容 Swift 5?升级 Swift 5 基本不会有大问题,但当你用 switch 语句去枚举一个未知值的时候可能会遇到很多警告。。
其他未知值的警告
到底是什么问题呢?我现在用一小段代码展示为不同大小的类适配布局。它枚举出了 UIUserInterfaceSizeClass
的 所有可能值:
func configure(for sizeClass: UIUserInterfaceSizeClass) {
switch sizeClass {
case .uppercased:
// ...
case .compact:
// ...
case .regular:
// ...
}
}
当我把项目升级到 Swift 5 之后,Xcode 会报出一些警告:
警告内容:
Switch 语句涵盖了所有 case,但是
UIUserInterfaceSizeClass
以后可以会添加一些其他未知的值请使用 “@unknown default” 来处理未知值
如果你点击了修复,Xcode 会自动添加一个 @unknown default:
的 case:
switch sizeClass {
case .unspecified:
// ...
case .compact:
// ...
case .regular:
// ...
@unknown default:
fatalError()
}
你可以修改 fatalError()
,但这到底是什么意思呢?
冻结和非冻结的枚举
Swift 中的一个 switch
语句必须是详尽的或者包含一个默认的 default case 来处理其他所有情况。我原来的 switch 语句把在 Apple 在 iOS 12 的 UIKit
中定义的 UIUserInterfaceSizeClass
的所有可能值都进行了枚举。
如果 Apple 在 iOS 13 中引入了 .tiny
或 .large
,那会发生什么呢?使用新版本的 SDK 编译时,编译器会因 switch 语句不再详尽而报错。解决错误的的一种方法是引入一个 default:
case。我可以重写我的 switch 语句:
switch sizeClass {
case .compact:
// 布局为 compact 该做的事
default:
// 默认情况下该做的事
// 其中也包含了一些未知情况
}
该 switch 不再详尽,但它能处理所有现在已知和未来的未知的情况。这一点很好,但如果 switch 是详尽的,这对编译器就有优势,并且它在编译时能兼容二进制库。当枚举更新后有新的 case,你可能还需要警告。
Swift 进化的提案 SE-0192 添加了 @unknown default:
语法,这允许你可以继续使用一个详尽的 switch 来用于未来可能出现的情况:
switch sizeClass {
case .unspecified:
// ...
case .compact:
// ...
case .regular:
// ...
@unknown default:
// ...
}
@unknown default:
这个 case 只能用于让枚举变得详尽,并匹配添加到枚举的任何新案例。它依旧会为这些新 case 生成警告,以便你可以决定采取怎么样的操作。这不同于使用 default:
和一个非详尽的枚举,它不会提示你有新的 case 未处理。
冻结枚举
这一变化还增加了 冻结枚举 的概念,这不是为了获得任何新 case。它仅适用于在 Swift 里导入 C 或 Objective-C 的枚举。举个例子,标准库中的 ComparisonResult
。这是他在 Objective-C 中的定义:
typedef NS_CLOSED_ENUM(NSInteger, NSComparisonResult) {
NSOrderedAscending = -1L,
NSOrderedSame,
NSOrderedDescending
};
只有三种可能的情况,所以这个枚举永远不会改变。注意 NS_CLOSED_ENUM
注释而不是通常的 NS_ENUM
。我们并不需要在 switch 里添加 @unknown default
来让它变得详尽:
let result: ComparisonResult = ...
switch result {
case .orderedAscending:
// ...
case .orderedSame:
// ...
case .orderedDescending:
// ...
}
如果库的作者将新 case 添加到了冻结枚举,则编译会报错。
冻结还是非冻结?
我们不知道 Apple 是否计划在 UIKit
和相关框架中枚举的情况。大多数可能是非冷冻的,但在某些情况下,冷冻可能是有意义的。
例如,堆栈视图轴是一个 UILayoutConstraintAxis
的枚举,在 iOS 12 中仍未冻结。这是 Objective-C 的头文件(注意 NS_ENUM
):
typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {
UILayoutConstraintAxisHorizontal = 0,
UILayoutConstraintAxisVertical = 1
};
这意味着如果要打开堆栈视图轴,则需要允许将来可能的未知情况:
switch stackView.axis {
case .horizontal:
// ...
case .vertical:
// ...
@unknown default:
// ...
}
也许 Apple 会将其更改为 NS_CLOSED_ENUM
,又或者堆栈视图是否会在 iOS 13 有别的可能值?
我们需要做什么?
- 首先,你不需要改变你自己原先的 Swift 枚举。当你升级到 Swift 5 时,你不需要一开始就手动在代码上添加
@unknown default:
。Xcode 会发出警告来提示你需要添加的位置。 - 此更改仅适用于标准库和其他框架中的 C 语言枚举。
- 如果您的 switch 包含
default:
case,那就不需要进行任何变化。 - 彻底切换 C 语言的非冻结枚举,包括所有已知的情况(不含
default:
)是 Swift 5 中的警告。 - 你可以让 Xcode 帮你自动修复代码,它会添加一个
@unknown:
的 case 并消除警告。
阅读更多
有关更多详细信息,请参阅 Swift 进化的提案:
如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。