4a3fda41b48866c549d838aebdf15d6a
Swift 5 & 5.1 为我们带来了什么

WWDC 2019 Session 402: What's New in Swift ?
Session 演讲者:Ted Kremenek, Anna Zaks
文章作者:Not_Found
文章校对:Lefe_x

这个 Session 分为两个部分,前半部分会简单介绍一下宏观上的优化(包体积的减少,启动速度的提升等),及一些开源工具,后半部分主要是一些 Swift 5 及 Swift 5.1 带来的新特性。

宏观上的优化

ABI 的稳定

ABI 是什么

ABI 代表应用程序二进制接口,它定义了编译后的代码在运行时如何与其他库通信的细节。例如函数如何调用,数据如何在内存中呈现,哪些元数据可用,以及如何访问等等,这些细节对于编译后的代码来说都是必需的。

带来的变化:

1.Swift 项目与使用的核心库不必须使用相同的编译器进行编译

在 ABI 稳定前

如果一个 Swift 项目使用了一个 Swift 编写的核心库,因为项目和库是分开编译的,为了保证在运行时项目和库有一个兼容 ABI 去调操作系统的接口,最简单的方法,就是约束项目和库使用的编译器是相同的版本。

在 ABI 稳定后

在 Swift 5 以后,不同版本的 Swift 编译器使用的 ABI 都相同了,项目和库使用的 Swift 版本就不受约束了。

2.Module 稳定性

在 Swift 中,一个库和它导出的 API 接口列表称为一个 Module 。

在 Swift 5.1 以前,如果项目中引用了一个 Module ,编译时,会去读取 . Swift module 文件,里面会包含 Module 的 API 列表,因为 Module 的底层实现比较复杂,跟编译器耦合比较严重,所以项目使用的编译器必须和 Module 之前编译构建时使用的编译器版本相同,才能运行。通俗的来说,就是你开发一个库,你还得使用不同版本的 Swift 编译器对代码进行编译生成 framework ,这样使用特定 Swift 版本的项目才能集成。

在 Swift 5.1 以后,构建 Module 时,会生成一个 . Swift interface 文件,这个文件里面会包含 Module 的稳定的API 列表,这样使用 Module 的项目的编译器版本不必与 Module 的保持一致了。

所有App共享Runtime库

目前 Swift 的 Runtime 库已经集成到了所有的苹果操作系统中,所有 App可以共享同一个 Runtime 库(而不用将 App 使用的 Swift Runtime 在编译时绑定到每个 App 的 ipa 包中去,减小了包体积)。

1.包体积会变小

当用户在 iOS 12.2 及以后的系统中下载 App 时,下载的 App 会是不包含 Swift Runtime 库的,所有 App 共享同一个 Swift Runtime 库,可以节约下载流量。当用户在 iOS 12.2 以前的系统中下载 App 时, App 会是包含 Swift Runtime 库的版本。( Xcode 打包后会有一个 App Thining 的选项,在这一步会对App生成两种可执行文件,包含 Swift Runtime库的和不包含 Swift Runtime 库的,供不同 iOS 系统的 App Store 使用)。

2. App 启动时间的优化

一个使用 Swift 编写没有任何功能的 App ,只是启动 App ,在非共享 Swift Runtime 库时,大概有 5% 的时间需要用于处理 App 内置的 Swift Runtime
库。

在 Swift 5 之后,由于使用共享 Swift Runtime 库时,这部分的时间就降低为0 了,所以可以缩短 App 启动时间。

代码大小的优化

Swift 5 之后,构建的代码大小会减少 10% 。
如果在 Xcode 开启 “optimize for size” 选项,构建的代码大小会减少 15% 。

Swift 和Objective-C混编的优化

NSDictionary 向 Dictionary 之间的转换比以前快了 1.6 倍。
String 往 NSString 的转换比以前快了 15 倍。

首选的字符串编码从 UTF-16 切换到 UTF-8

可以看看这篇文章《UTF-8 String》,里面有详细介绍。

Swift 工具和开源

Swift Docker Image

跟大部分开发者没有关系,参与 Swift 开发,贡献代码的人需要了解一些,大致是在 Docker Hub 创建了一个 Swift 的 Docker 镜像,降低了参与 Swift 开发的门槛。

Sourcekitd

SourceKit 是一套工具集,使得大多数 Swift 源代码层面的操作特性得以支持,例如源代码解析、语法高亮、排版(typesetting)、自动补全、跨语言头文件生成等等。
Sourcekitd 是一个对 SourceKit 进行压力测试的工具,以便于发现 Bug ,并进行修复。

可以看看这两篇文章,进行详细了解。
Introducing the sourcekitd Stress Tester
Translation - 起底 SourceKit

SourceKit-LSP

LSP(Language-Server-Protocol)是由红帽、微软和 Codenvy 联合推出的一个开源的语言服务器协议。可以让各种IDE便捷地支持各种程序代码的开发。

通俗的来说,就是 Swift 也支持这个 LSP 协议,并且为此写了一个项目SourceKit-LSP ,便于在 VSCode ,Vim 等其他遵循 LSP 协议的 IDE 中进行 Swift 项目开发,Session 里面演示了在 Vim 中进行 Swift 项目开发。

想要了解更多可以看看下面这两篇文章:
项目地址
VSCode 使用 LSP 进行 Swift 开发

Swift 5 及 Swift 5.1的一些新特性

这一部分主要讲的是 Swift 5 和 Swift 5.1 的具体特性更新, Swift-evolution 这个项目里面包含了 Swift 所有特性更新和介绍。

Swift-evolution 的 Github 地址
Swift-evolution 的网站主页

单表达式函数的隐式返回

最低支持版本: Swift 5.1

如果一个闭包只包含一个表达式,那么可以把 return 省略掉,隐式返回该表达式(我们经常见到的map, filter, flatmap 这些高阶函数经常用到这个特性)。在 Swift 5.1 中,一个函数只包含一个表达式,也可以将 return 省略掉。

举例说明:
没有这个新特性以前:

struct Rectangle {
    var width = 0.0, height = 0.0
    var area: Double {
            return width * height
    }
}

有这个新特性以后,可以写成这样

struct Rectangle {
    var width = 0.0, height = 0.0
    var area: Double { width * height }
}

想要了解更多可以看看这个详细介绍

根据默认值合成结构体初始化方法

最低支持版本: Swift 5.1

一个 Struct 的各个属性有默认值时,编译器会基于属性逐一去生成初始化方法(PS: 提出这个新特性的开发者叫 Alejandro Alonso ,刚刚高中毕业)。

举例说明:

struct Dog {
  var age = 0
  var name = "Generic dog name"
}

没有这个特性以前,编译器默认会生成这两个初始化方法

func Dog()
func Dog(age:Int, name: String)

执行这两行代码进行初始化是没有问题的

let boltNewborn = Dog()
let daisyNewborn = Dog(name: "Daisy", age: 0) 
let benjiNewborn = Dog(name: "Benji")

`
但是如果执行这行代码进行初始化,会报 "cannot invoke initializer for type 'Dog' with an argument list of type '(name: String)'" 错误,因为编译器没有自动生成 func Dog(name: String) 方法
//有了新特性以后,会自动生成 func Dog(name: String) 方法,调用这个方法进行初始化时,没有传入的属性会被赋以默认值,也就是执行下面的代码不会报错,生成的 benjiNewborn 对象的 age 会是0 , name 会是 Benji

let benjiNewborn = Dog(name: "Benji")

想要了解更多可以看看这个详细介绍

SIMD — 用于矢量编程的更好的API

最低支持版本: Swift 5

这个特性主要跟图形计算比较相关,大部分开发者可能用不上,或者也是使用 C/C++ 进行这方面的计算,所以感兴趣的朋友可以自己看看这个详细介绍

字符串插值的新设计

最低支持版本: Swift 5

1.在字符串插值方面,比 Swift 4.2 中的快1.7 倍。
2.扩大了字符串插值的应用范围。

在没有这个新特性以前,这样写是错误的,因为插值发生在翻译之前,所以不能这么写。

let quantity = 10
label.text = NSLocalizedString(
    "You have \(quantity) apples,
    comment: "Number of apples"
)
label.text = String(format: formatString, quantity)

在没有这个新特性以前,如果想要在 NSLocalizedString 中传值,必须写成下面这样。
```
let quantity = 10
label.text = NSLocalizedString(

top Created with Sketch.