[譯] Swift 5 中的枚舉凍結

你是否已經將你的 Xcode 工程升級到了兼容 Swift 5?升級 Swift 5 基本不會有大問題,但當你用 switch 語句去枚舉一個未知值的時候可能會遇到不少警告。。前端

其餘未知值的警告

究竟是什麼問題呢?我如今用一小段代碼展現爲不一樣大小的類適配佈局。它枚舉出了 UIUserInterfaceSizeClass全部可能值android

func configure(for sizeClass: UIUserInterfaceSizeClass) {
    switch sizeClass {
    case .uppercased:
        // ...
    case .compact:
        // ...
    case .regular:
        // ...
    }
}
複製代碼

當我把項目升級到 Swift 5 以後,Xcode 會報出一些警告:ios

additional unknown values warning

警告內容:git

Switch 語句涵蓋了全部 case,可是 UIUserInterfaceSizeClass 之後能夠會添加一些其餘未知的值github

請使用 「@unknown default」 來處理未知值objective-c

若是你點擊了修復,Xcode 會自動添加一個 @unknown default: 的 case:swift

switch sizeClass {
case .unspecified:
    // ...
case .compact:
    // ...
case .regular:
    // ...
@unknown default:
    fatalError()
}
複製代碼

你能夠修改 fatalError(),但這究竟是什麼意思呢?後端

凍結和非凍結的枚舉

Swift 中的一個 switch 語句必須是詳盡的或者包含一個默認的 default case 來處理其餘全部狀況。我原來的 switch 語句把在 Apple 在 iOS 12 的 UIKit 中定義的 UIUserInterfaceSizeClass 的全部可能值都進行了枚舉。app

若是 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 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索