SwiftUI 和 Swift 5.1 新特性之:不透明返回類型

今年 WWDC 最重要的關注點是什麼?Swift!Swift 5.0 ABI 達到穩定,Swift 5.1 達到 Module Stability,預示着 Swift 進入了成熟期。蘋果也開始認真地吃自己的狗食了,我們看到這屆大會上推出了幾個用 Swift 寫的 iOS 框架,佔篇幅最大的,無疑是 SwiftUI。爲了這個框架寫得6,蘋果可以改語言,還不止一處。這次給大家介紹的是 Swift 5.1 在 protocol 上的改進:Opaque Result Type 不透明結果類型。這個特性增強了 Swift 泛型的能力,影響了 SwiftUI 的設計。

不透明結果類型新特性

先來看一段代碼,它展現了原來 protocol 能力上的缺陷:

protocol Shape {}

struct Rectangle: Shape {}

struct Union<A: Shape, B: Shape>: Shape {
    var a: Shape
    var b: Shape
}

struct Transformed<S: Shape>: Shape {
    var shape: S
}

protocol GameObject {
    associatedtype ShapeType: Shape
    var shape: ShapeType { get }
}

struct EightPointedStar: GameObject {
    var shape: Union<Rectangle, Transformed<Rectangle>> {
        return Union(a:Rectangle(), b:Transformed(shape: Rectangle()))
    }
}

缺陷有兩方面:

  1. 上述代碼是可以編譯通過的,但是 EightPointedStar 的 shape 返回類型又臭又長,被暴露了出去;如果換成 Shape 則編譯不通過,原因是 associatedtype ShapeType 要求必須指定具體的類型,而 Shape 不實現 Shape 本身。

  2. 假如 Shape 協議中含有 Self 或者 associatedtype,無法作爲函數的返回參數。這是 Swift 泛型系統長久以來的一個問題。

而本文介紹的 Swift 5.1 Opaque Result Type 特性,解決了上述問題,它爲 protocol 作爲返回類型提供以下能力:

  1. 語法上隱藏具體類型,所以叫做不透明結果類型

  2. 強類型:類型參數不丟失

  3. 允許帶有 Self 或者 associatedtype 的 protocol 作爲返回類型

在 Swift 5.1 中,將返回類型改成 some + protocol 的形式:

struct EightPointedStar: GameObject {
    var shape: some Shape {
        return Union(a:Rectangle(), b:Transformed(shape: Rectangle()))
    }
}

這類的泛型特性也被稱作“反向泛型”,因爲具體的類型參數是由“實現部分”指定並隱藏起來的,而一般的泛型是由“調用者”所指定的。

上面這個例子中:語法上隱藏具體類型很明顯,再舉一個例子說明其它 2 個特性:

func foo<T: Equatable>(x: T, y: T) -> some Equatable {
    let condition = x == y
    return condition ? 42 : 11
}

let x = foo("apples", "bananas")
let y = foo("apples", "oranges")

print(x == y) // 這裏可以被調用是因爲泛型系統保留了強類型

這個例子顯示了不透明結果類型的三個特性:既對外隱藏了具體的 Equatable 類型;又保留了強類型(使得 x == y)可以比較;還支持了 Equatable 這個帶 Self 的泛型約束。

不透明結果類型對於函數實現有一個增強的要求:函數實現必須返回同一個具體類型,以上述代碼爲例:不能返回 Equatable 或者是 不同類型的 Equatable 的實現。

這裏還有一個小問題:既然 x 和 y 可以直接比較,那麼它們可否直接賦值給 var i: Int 呢?答案是對於靜態類型系統是不可以的,它保留了 some Equatable 的具體類型隱藏功能,但是如果使用動態類型判斷 as? Int,則可以轉換成 Int。

SwiftUI 上的應用


SwiftUI 的一大特點是高度可組合,View 的唯一屬性 body 是另一個滿足 View 約束的具體 View 類型,我們在這裏看到了組合以及遞歸兩個特性。下面來看一個具體的 View 類型:

這個 OrderCell 使用了不透明返回類型的特性,對外隱藏了具體類型 HStack。我們看到 OrderCell 的類型它是一個遞歸的定義。

所有的遞歸定義都需要一個終止條件,於是就有了以下這些 Primitive Views:

結語

很高興看到蘋果終於開始提供 iOS 操作系統中的 Swift-Only 的 Framework,它對於 Swift 的推廣和語言改進有進一步的促進作用。

另外,我們也要注意到這個特性增加了 Swift ABI 的能力,需要最新的 runtime 才能運行。

下一次,我們聊一聊和 SwiftUI 相關的另一個 Swift 新特性:Property Wrapper Types

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章