原文链接:https://juejin.im/post/5cfb1217f265da1b8a4f0f14
今年 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 上的应用,加深对它的理解。
先来看一段代码,它展现了原来协议能力上的缺陷:
- 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()))
- }
- }
- 复制代码
缺陷有两方面:
而本文介绍的 Swift 5.1 Opaque Result Type 特性,解决了上述问题,它为协议作为返回类型提供以下能力:
在 Swift 5.1 中,将返回类型改成 some + 协议名称的形式:
- 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 中的视图类型的基本定义是一个协议 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):
所有的递归定义都需要一个终止条件,于是就有了以下这些原生 View:Text、Color、Spacer、Image、Shape、Divider等
很高兴看到苹果终于开始提供 iOS 操作系统中的 Swift-Only 的 Framework,它对于 Swift 的推广和语言改进有进一步的促进作用。
另外,我们也要注意到这个特性增加了 Swift ABI 的能力,需要最新的 runtime 才能运行。