譯自 www.hackingwithswift.com/articles/21…git
更多內容,歡迎關注公衆號 「Swift花園」github
喜歡文章?不如來個 🔺💛➕三連?關注專欄,關注我 🚀🚀🚀編程
Swift 5.3 有很多變化,這其中包括多模式 catch 語句,多拖尾閉包,以及 Swift Package Manager 的一些重要改變。swift
本文會帶你瀏覽一些主要的變化,同時提供參考代碼,以便你能夠自行嘗試。如下是要介紹的新特性的清單:數組
where
語句didSet
語義SE-0276 引入了一個能夠在單個 catch
塊中捕獲多個錯誤 case 的特性,這能讓咱們免除錯誤處理時的重複代碼。安全
例如,下面的代碼用枚舉定義了錯誤的兩種狀況:微信
enum TemperatureError: Error {
case tooCold, tooHot
}
複製代碼
當咱們讀取到溫度時,既能夠拋出兩種錯誤中的某一個,也能夠返回 「OK」:閉包
func getReactorTemperature() -> Int {
90
}
func checkReactorOperational() throws -> String {
let temp = getReactorTemperature()
if temp < 10 {
throw TemperatureError.tooCold
} else if temp > 90 {
throw TemperatureError.tooHot
} else {
return "OK"
}
}
複製代碼
在捕獲錯誤的環節,SE-0276 容許咱們用逗號分隔來表示咱們要以相同方式處理 tooHot
和 tooCold
。app
do {
let result = try checkReactorOperational()
print("結果: \(result)")
} catch TemperatureError.tooHot, TemperatureError.tooCold {
print("關閉反應堆")
} catch {
print("未知錯誤")
}
複製代碼
處理的 case 能夠是任意數量的。框架
SE-0279 引入了多拖尾閉包,這使得調用包含多個閉包的函數能夠更簡單地實現。
這個特性在 SwiftUI 中很是受歡迎。原來形以下面這樣的代碼:
struct OldContentView: View {
@State private var showOptions = false
var body: some View {
Button(action: {
self.showOptions.toggle()
}) {
Image(systemName: "gear")
}
}
}
複製代碼
能夠被改寫成:
struct NewContentView: View {
@State private var showOptions = false
var body: some View {
Button {
self.showOptions.toggle()
} label: {
Image(systemName: "gear")
}
}
}
複製代碼
理論上並不要求 label:
要跟在前一個閉包的 }
後面,因此你甚至能夠像下面這樣書寫:
struct BadContentView: View {
@State private var showOptions = false
var body: some View {
Button {
self.showOptions.toggle()
}
label: {
Image(systemName: "gear")
}
}
}
複製代碼
不過,我會建議你小心代碼的可讀性 —— 像上面的 label
那樣的代碼塊,在 Swift 裏看起來更像是標籤化的代碼塊,而不是 Button
構造器的第二個參數。
注: 有關 Swift 的多拖尾閉包特性的討論很是熱烈。我想提醒你們的是,像這種類型的語法變更一開始看起來可能會有點彆扭,咱們須要耐心,給它時間,在實踐中體會它帶來的結果。
SE-0266 使得咱們能夠爲枚舉生成 Comparable
實現,同時不要求咱們聲明關聯值,或者要求關聯值自己必須是 Comparable
的。這個特性讓咱們能夠在同類型的枚舉之間用 <
,>
和相似的比較操做符來進行比較。
例如,假設咱們有一個枚舉,它描述了衣服的尺寸,咱們能夠要求 Swift 爲它自動生成 Comparable
實現,代碼以下:
enum Size: Comparable {
case small
case medium
case large
case extraLarge
}
複製代碼
而後咱們就能夠建立兩個這個枚舉的實例,而且用 <
進行比較:
let shirtSize = Size.small
let personSize = Size.large
if shirtSize < personSize {
print("T恤 過小了!")
}
複製代碼
自動生成的實現,也能很好地適應枚舉的 Comparable
關聯值。例如,假設咱們有一個枚舉,描述了某個隊伍獲取世界盃冠軍的次數,代碼能夠這樣實現:
enum WorldCupResult: Comparable {
case neverWon
case winner(stars: Int)
}
複製代碼
而後咱們用不一樣的值來建立枚舉的不一樣實例,而且讓 Swift 對它們進行排序:
let americanMen = WorldCupResult.neverWon
let americanWomen = WorldCupResult.winner(stars: 4)
let japaneseMen = WorldCupResult.neverWon
let japaneseWomen = WorldCupResult.winner(stars: 1)
let teams = [americanMen, americanWomen, japaneseMen, japaneseWomen]
let sortedByWins = teams.sorted()
print(sortedByWins)
複製代碼
排序過程會把未得到世界盃冠軍的隊伍放在前面,而後是日本女子隊,再而後是美國女子隊 —— 兩組 winner
的隊被認爲是大於兩組 neverWon
的隊,而 winner(stars: 4)
被認爲是大於 winner(stars: 1)
。
SE-0269 使得咱們能夠在一些沒必要要的地方省略 self
。在這個改變以前,咱們須要在全部的閉包當中對引用 self
的屬性或者方法冠以 self.
,以便顯式地明確語義。但有的時候因爲閉包不可能產生引用循環,self
是多餘的。
例如,以前咱們須要把代碼寫成下面這樣:
struct OldContentView: View {
var body: some View {
List(1..<5) { number in
self.cell(for: number)
}
}
func cell(for number: Int) -> some View {
Text("Cell \(number)")
}
}
複製代碼
對 self.cell(for:)
的調用不會產生引用循環,由於它是在結構體內使用。多虧了 SE-0269,上面的代碼如今能夠免去 self.
:
struct NewContentView: View {
var body: some View {
List(1..<5) { number in
cell(for: number)
}
}
func cell(for number: Int) -> some View {
Text("Cell \(number)")
}
}
複製代碼
這個特性對於大量使用閉包的框架很是有用,包括 SwiftUI 和 Combine。
SE-0281 引入了一個新的 @main
屬性,它可讓咱們聲明程序的入口。這個特性使得咱們能夠精確地控制程序啓動時要執行的代碼,對於命令行程序尤爲有幫助。
例如,當咱們建立一個終端應用時,咱們必須建立一個叫 main.swift 的文件,而後把啓動代碼放在裏面:
struct OldApp {
func run() {
print("Running!")
}
}
let app = OldApp()
app.run()
複製代碼
Swift 會自動把 main.swift 看做最頂層的代碼,建立 App
實例而且運行。即使在 SE-0281 以後這個作法都一直被延續,但如今你能夠幹掉 main.swift 了,轉而使用 @main
屬性來標記某個包含靜態 main
方法的結構體或者類,讓它充當程序入口:
@main
struct NewApp {
static func main() {
print("Running!")
}
}
複製代碼
上面的代碼所在的程序運行時,Swift 會自動調用 NewApp.main()
來啓動程序流程。
新的 @main
屬性對於 UIKit 和 AppKit 開發者來講可能有點屬性,由於咱們正是用 @UIApplicationMain
和 @NSApplicationMain
來標記 app 代理的。
不過,使用 @main
的時候有一些注意事項:
@main
屬性@main
屬性只能用在最頂層的類型上 —— 這個類型不繼承自任何其餘類where
語句SE-0267 引入一個新特性,你能夠給泛型類型或者擴展添加帶有 where
語句限定的函數。
例如,咱們建立了一個簡單的 Stack
,能夠壓棧,出棧元素:
struct Stack<Element> {
private var array = [Element]()
mutating func push(_ obj: Element) {
array.append(obj)
}
mutating func pop() -> Element? {
array.popLast()
}
}
複製代碼
藉助 SE-0267,咱們如今能夠添加一個 sorted()
方法給這個 Stack
,而且要求這個方法只有在 Stack
的泛型參數 Element
遵循 Comparable
協議的時候才能使用:
extension Stack {
func sorted() -> [Element] where Element: Comparable {
array.sorted()
}
}
複製代碼
SE-0280 使得枚舉能夠參與 protocol witness matching,這是一種表述咱們能夠更容易地匹配協議要求的技術方式。
例如,你能夠編寫代碼處理各類類型的數據,可是假如數據不見了怎麼辦呢?固然,你能夠藉助空合運算符,每次都提供一個默認值。不過。你能夠藉助協議來要求默認值,而後讓各類類型遵循這個協議:
protocol Defaultable {
static var defaultValue: Self { get }
}
// 讓整數有默認值 0
extension Int: Defaultable {
static var defaultValue: Int { 0 }
}
// 讓數組有默認值空數組
extension Array: Defaultable {
static var defaultValue: Array { [] }
}
// 讓字典有默認值空字典
extension Dictionary: Defaultable {
static var defaultValue: Dictionary { [:] }
}
複製代碼
SE-0280 使得咱們能對枚舉作出同樣的控制。好比,你有一個 padding
枚舉,它能接收像素值,釐米值,或者是系統的默認值:
enum Padding: Defaultable {
case pixels(Int)
case cm(Int)
case defaultValue
}
複製代碼
這樣的代碼在 SE-0280 以前是沒法實現的 —— Swift 會抱怨 Padding
不知足協議。可是,若是你仔細琢磨一下,協議實際上是知足的:咱們須要一個靜態的 defaultValue
,它返回 Self
,換言之,就是某個遵循協議的具體類型,而這正是 Padding.defaultValue
提供的。
didSet
語義SE-0268 調整了 didSet
屬性觀察者的工做方式,以便它們能更高效地工做。對於這個優化你不須要改動任何代碼,自動得到一個小小的性能提高。
在內部,Swift 作出的改變是在設置新值時再也不查詢舊值。若是你不使用舊值,也沒有設置 willSet
,Swift 會即時修改數值。
假如你須要用到舊值,只須要引用 oldValue
便可,方式以下:
didSet {
_ = oldValue
}
複製代碼
SE-0277 引入了一個新的半精度浮點類型,Float16
。這個精度在圖像編程和機器學習中十分常見。
新類型和 Swift 原來的其餘類型類似:
let first: Float16 = 5
let second: Float32 = 11
let third: Float64 = 7
let fourth: Float80 = 13
複製代碼
Swift 5.3 爲 Swift Package Manager (SPM) 帶來了不少提高,恕我不能在這裏一一舉例。不過咱們能夠討論一下SPM 有哪些變化以及爲何會有這些變化。
首先,SE-0271 (Package Manager Resources) 使得 SPM 能包含諸如圖片,音頻,JSON 等類型的資源。這個機制可不僅是把文件拷進最終的 app bundle 這麼簡單 —— 舉個例子,咱們能夠應用一個自定義處理步驟到咱們的 assets,好比爲 iOS 優化圖片。爲此,新增的 Bundle.module
屬性就是用來在運行時訪問這些 assets 的。SE-0278 (Package Manager Localized Resources) 進一步支持了資源的本地化版本,例如提供適用某個國家的圖片。
其次,SE-0272 (Package Manager Binary Dependencies) 使得 SPM 可使用二進制包。這意味着像 Firebase 這樣的閉源 SDK 如今也能夠經過 SPM 集成了。
再次,SE-0273 (Package Manager Conditional Target Dependencies) 可讓咱們指定爲特定平臺和配置使用依賴。例如,咱們可能在爲 Linux 平臺編譯時,額外須要某些特定的框架,或者咱們在本地測試的時候須要一些依賴調試用的框架。
值得一提的是,SE-0271 的 「Future Directions」 一節中提到了對資源的安全訪問 —— 這意味着像 Image("avatar")
這樣的代碼以後會變成 Image(module.avatar)
。
個人公衆號 這裏有Swift及計算機編程的相關文章,以及優秀國外文章翻譯,歡迎關注~