SwiftUI 和 Swift 5.1 新特性(1) some + 協議名稱做爲返回類型

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

本文咱們先了解 Swift 5.1 的不透明返回類型的用途,而後再經過 Swift UI 上的應用,加深對它的理解。swift

1. 不透明結果類型新特性

先來看一段代碼,它展示了原來協議能力上的缺陷:app

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. 上述代碼是能夠編譯經過的,可是 EightPointedStarShape 返回類型又臭又長,被暴露了出去;若是換成 Shape 則編譯不經過,緣由是 associatedtype ShapeType 要求必須指定具體的類型,而 Shape 不實現 Shape 自己。ide

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

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

  1. 語法上隱藏具體類型,因此叫作不透明結果類型spa

  2. 強類型:類型參數不丟失操作系統

  3. 容許帶有 Self 或者 associatedtype 的協議做爲返回類型設計

在 Swift 5.1 中,將返回類型改爲 some + 協議名稱的形式:

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

這類的泛型特性也被稱做「反向泛型」,由於具體的類型參數是由「實現部分」指定並隱藏起來的,而通常的泛型是由「調用者」所指定的。

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

// 這個例子在Xcode 11 beta 2 以後能 work
func foo<T: Equatable>(_ x: T, _ y: T) -> some Equatable {
    let condition = x == y
    return condition ? 42 : 11
}

func test() {
  let x = foo("apples", "bananas")
  let y = foo("apples", "oranges")
  print(x == y) // 這裏能夠被調用是由於泛型系統保留了強類型
}
複製代碼

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

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

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

2. 在SwiftUI 上的應用

SwiftUI 中的視圖類型的基本定義是一個協議 View

public protocol View : _View {
    // body 屬性的類型
    associatedtype Body : View

    // 惟一屬性 body 的類型是另外一個具體類型 View
    var body: Self.Body { get }
}

複製代碼

SwiftUI 最大特色的是聲明式以及高度可組合,View 的惟一屬性 body 是另外一個知足 View 約束的具體 View 類型,咱們在這裏看到了組合以及遞歸兩個特性。下面來看一個具體的 View 類型 ContentView

struct ContentView : View {
    var body: some View {
      VStack {
        Text("Hello World")
        Text("Love & Peace")
      }
    }
}
複製代碼

ContentView 使用了不透明結果類型的特性,對外隱藏了具體類型 VStack。此外,ContentView 的具體類型都是經過它的 body 屬性遞歸定義的(取決於它所包含的具體 View):

全部的遞歸定義都須要一個終止條件,因而就有了如下這些原生 ViewTextColorSpacerImageShapeDivider

結語

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

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

掃描下方二維碼,關注「面試官小健」

相關文章
相關標籤/搜索