添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
let harry = User(firstName: "Harry", lastName: "Potter")
let hermione = User(firstName: "Hermione", lastName: "Granger")
let ron = User(firstName: "Ron", lastName: "Weasley")
let users = [harry, hermione, ron]

現在,如果我們想取得一個包含所有使用者名字 (firstName) 的陣列,我們像這樣做:

let oldFirstNames = users.map { $0.firstName }

但是,有了 Swift 5.2 Keypath,我們就可以這樣做:

let newFirstNames = users.map(\.firstName)

我們也可以在 compactMap filter 上使用它,就像是這樣:

let lastNames = users.compactMap(\.lastName)

使用者定義類型的可呼叫值 (Callable Value)

Swift Evolution 提案 SE-0253 在 Swift 引入了一個「靜態」可呼叫值。可呼叫值是以類似函數行為定義的值,可以被函數 syntax 所呼叫。

讓我們看看以下範例:

struct Adder {
    var base: Int
func callAsFunction(_ x: Int) -> Int {
      return x + base
var adder = Adder(base: 3)
adder(10) // returns 13
adder.callAsFunction(10) // returns 13

在範例中可以看到,如果一個值的型別實作了 callAsFunction() 方法,我們就可以直接呼叫該值。

我們可以按需要添加無限多的參數 (parameter),也可以控制回傳值,有需要的話,我們甚至可以將方法標記為 mutating 。而且,我們更可以在單個型別上定義多個 callAsFunction 方法。

請注意, callAsFunction 與 Swift 5 推出的 @dynamicCallable 並不一樣:

當我們宣告一個 call-as-function 方法,我們會指定引數 (arguments) 的數量,以及其型別和標籤 (label)。但當我們宣告 dynamicCallable 屬性的方法時,我們只會指定用於保存引數陣列的型別。

Subscript 現在可以宣告預設引數

現在,在添加客製化 subscript 到型別時,我們可以將預設引數用於任何參數。

例如,我們有一個 Hogwarts 結構,並準備要定義客製化 subscript 來取得特登學生資料,當有人嘗試訪問不存在的索引時,我們就可以回傳的一個預設值:

struct Hogwarts {
    var students: [String]
subscript(index: Int, default default: String = "Unknown") -> String {
        if index >= 0 && index < students.count {
            return students[index]
        } else {
            return `default`
let school = Hogwarts(students: ["Harry", "Hermione", "Ron"])
print(school[0])
print(school[5])

以上結果將會印出在索引 0 的 Harry ,而因為索引 5 沒有學生,結果會印出 Unknown

如果我在 subscript 使用了 default default ,就可以使用這樣的一個客製化值:

print(school[-1, default: "Draco"])

對 Module 外定義的子類別繼承添加限制

在 Swift 5.2,有一個小改變可以會破壞以前的功能。

現在,如果一個超類別 (superclass) 已經在其他 Module 中被定義了,並擁有一個 非公開的指定初始器 (non-publice designated initializers) ,其子類別就無法自動從超類別繼承便捷初始化器 (convenience initializer)。

要恢復這種自動繼承行為,基底類別 (base class) 必須確保所有指定初始器都是 public open

Lazy Filtering 的次序倒轉了

另一個可以破壞原本功感的改變,就是 Lazy Filtering。如果我們使用像是 array 的 Lazy Sequence,並應用多個過濾器,現在,這些過濾器將以相反的順序運行。

讓我們看看下面的例子,假設我們有一個名字陣列,我們把第一個我們應用第一個過濾器選擇以 H 開頭的名稱,然後應用第二個過濾器以獲取回傳 true 的名稱。

let people = ["Harry", "Hermione", "Ron"]
    .lazy
    .filter { $0.hasPrefix("H") }
    .filter { print($0); return true }
print(people.count)

在 Swift 5.2 或更新的版本中,結果將印出 “Harry” 和 “Hermione”,因為在第一個過濾器運行之後,只有這兩個名字剩下來,並進入第二個過濾器。但是,在 Swift 5.2 版本 之前 ,結果將返回所有名字,因為第二個過濾器會於第一個過濾器之前運行。

這是因為 Lazy Filtering,如果我們把 .lazy 拿走,無論是甚麼 Swift 版本,它都會印出 “Harry” 和 “Hermione”。

外部的預設值

在 Swift 5.2 之前的版本中,當內部函數 (inner function) 使用外部函數 (outer function) 引數作為默認引數 ( SR-2189 ) 時,編譯器通常會崩潰。

Swift 5.2發佈後,你可以成功運行以下程式碼,否則編譯時就會回傳 unsafeMutableAddressor 錯誤。

func outer(x: Int) -> (Int, Int) {
    func inner(y: Int = x) -> Int {
        return y
    return (inner(), inner(y: 0))

改善了的新診斷功能

Swift 5.2大大提高了 Swift 編譯器中錯誤訊息的質量和準確性。

舉個例子:

import SwiftUI
struct RoomDetails: View {
  @State var roomName: String
  @State var imageName: String
var body: some View {
    VStack {
      TextField("Room Name")// It should be TextField("Name", text: $name)
Image(imageName)
        .frame(maxWidth: 300)

在以上的程式碼中,我們嘗試將 TextField 視圖綁定到一個 roomName @State。在 Swift 5.1,這會導致 frame() 修飾符出現錯誤,說明 “Int” 不能轉換為 “CGFloat”。但是 Swift 5.2 或更新的版本,就可以正確地識別這個錯誤,是呼叫中缺少參數文本的引數。

程式碼完成功能的改善

在程式碼完成功能方面,其結果的信息型別改善了。在可行的情況下,結果會顯示不透明的結果類型(例如 some View),並保留型別別名。結果將不再顯示非必需的父型別。

例如,在 Swift 5.1.3 (Xcode 11.3.1) 中: