Bc779350782c3fd1aebf5858c811c77d
WWDC20 10170 - What's new in Swift

引子

每一年的「What's new in ...」系列都会有很多提纲挈领的作用。Swift 这个逐渐完善的语言再今年也必会大放异彩,为我们开发者带来更多讨喜的新特性。在这个 Session 中,虽然无法得知 Swift 5.3 的所有进化细节,但是已经将 Swift 5.3 新特性中最重要的部分归纳到一起,让我们对新的 Swift 有一个更好的认识。

话不多说,来看正文。

本文知识目录

Apple 开发者生态总述

随着 ABI 逐渐稳定,Swift 5 将会有越来越多的强大的 API 被逐渐开放出来。SwiftUI 就是其中的代表之一。在过去的一年中,Swift 在上层和底层的 Apple API 的开发中都付出了很多精力,来构建更加完善的 Apple 开发生态。

当然,越来越多强大的功能也离不开 Xcode 这个开发工具Swift Package Manager 包管理器,这也是 Apple 开发生态的重要一环。与此同时,身为跨平台的 Swift 语言以及 SwiftUI 这套跨平台应用方案也是相当重要的一环。

所以在这篇 「What's new in Swift 」中,我们围绕着四个主题来一一讲述 Swift 最新两个发行版中的特性:

  • Swift 运行效率改善(Swift's Runtime Performance)
  • 开发者体验(Developer Experience)
  • 跨平台方案(Multi-platform)
  • 语言与库(Language and Libraries)

相信看完这些将会让你提纲挈领,了解新版 Swift 最重要的信息。

Swift 运行效率改善(Swift's Runtime Performance)

Code Size

Apple 官方持续观察 Swift 项目产物中 __text 的大小。以下纵轴的单位是 Objective-C 代码生成的 __text Section 的 Size,横轴对应的 Swift 版本,其体现的数值是 $Val=\frac{S_{Swift}}{S_{ObjC}}$。从 Swift 4 以来,一直到 5.3 版本,从一开始的 2.3 倍,如今已经优化到 1.5 倍。

但为什么 Swift 会需要这么多的 Code Size?官方解释到,这些差异是不可避免的,因为 Swift 带有一些“安全”相关的 Feature,这些在生成的产物中需要有一定量的代码实现,从而增加了 __text 这个 Section 的 Size。

当然不同体量的 App 会产生差异化的优化结果。例如在 Thomas Ricouard 的开源项目 MovieSwiftUI 中,使用 Xcode 12 的编译结果就比是用 11 版本的 __text 下降 43%。

Dirty Memory 使用率优化

对于一般的桌面操作系统,clean memory 可以认为是能够进行 Page Out 的部分。由于没有内存交换机制,对于 iOS 来说,clean memory 指的是能被重新创建的内存,它主要包含下面几类:

  • app 的二进制可执行文件
  • framework 中的 _DATA_CONST 段
  • 文件映射的内存
  • 未写入数据的内存

一句话来总结一下,dirty memory 就是指的是不能被系统回收的内存占用。那么在 Swift 5.3 中对于 dirty memory 有什么样的优化呢?

我们通过以下的 Mountain Model 以及其对应的数组它们的 Memory Layout 来举例说明。假如我们现在有这么一段 Mountain Model 的代码:

// Model 文件
@interface Mountain: NSObject
{
    NSUUID *uuid;
    NSString *name;
    float *height;
}
@end

// 其他引用 Model 的 Code
NSArray<Mountain*> *mountains = loadMountains();

我们知道 Objective-C 中的 NSArray 存储的是对应元素的指针,而且每个对象中非基本类型也都是通过指针的方式进行保存。但是由于 Tagged Pointer 的技术我们得知,某些 NSString 由于较短未超过 8 个字节,无需使用指针存储对象,这样就达到了轻微的优化。

而在 Swift 中,我们通过对于内存的观察发现其直接存储值(其优化类似于 Tagged Pointer)的最大上限变成了 16 个字节,15 位的长度组以应对更多的场景。另外,Swift 中的数组是直接对值进行存储,避免了通过指针访问其中许多值的需要。

于是, Mountain Model 的 Memory Layout 在 Objective-C 和 Swift 中会有如下的对比:

在这里使用了一个含有 400 个 model 对象的 demo,并测试了一下堆内存的使用情况。可以发现 Swift 5.3 已经优化到原来 Objective-C 下的 $\frac{1}{3}$。

这些优化虽然感觉在当前的设备和系统上微不足道,但是在旧的设备和系统中会有较为明显的体验优势。

Swift 标准库下沉

这个下沉是相对于 Foundation 而言,Swift Standard Library 现在变成了 Foundation 的依赖而存在。在后续会逐渐完善 Swift 对于 Foundation 的 API,那些只能通过 C 和 Objective-C 访问到的方法也会有迁移的计划。

开发者体验(Developer Experience)

诊断策略(Diagnostics)

在新版本的 Swift Compiler 中,更新了新的诊断策略方案,优化了问题路径的提示。在 Session 中提及到的两点优化:

  1. 使用全新定位代码问题的策略;
  2. 更加精确提示以及可行性更高的提示方案;

在诊断问题时,编译器也会记录更多信息。这些优化详情可以前往 swift.org 查看更多介绍:New Diagnostic Architecture Overview

代码补全(Code Completion)

在这个例子中可以看到,一个空缺的字典内,编译器可以推断三元表达式的值。

另外还有一个场景就是对于动态属性的补全,例如将 KeyPath 作为方法内的传参:

除了更强大的补全能力以外,在速度上 Xcode 12 也有大幅度的提高。在某些场景下(请注意这句话),其性能会强于 Xcode 11.5 高达 15 倍!

代码缩进(Code Indentation)

新版 Xcode 中的代码缩进也得到了显著的改善,这些改善已经合入了开源的 SourceKit 。对于缩进的修改有以下几个方面:

  1. 链式方法调用;
  2. 调用参数;
  3. 元祖元素;
  4. 跨多行的集合元素;
  5. 多行条件语句(ifguardwhile);

这里举一个 MovieSwiftUI 项目中的例子,来体现新版的链式调用改善后的缩进风格:

Debugging(调试)

首先我们知道 Swift 是通过 clang 的 Modules 方式从 Objective-C 代码中导入 API ,来解析相关类型和变量信息。在这个过程中,LLDB 需要导入当前调试上下文中所有可见的 Swift 模块和 clang Modules。

虽然这些 Module 文件有大量的类型描述信息,但由于 LLDB 会带有整个应用程序以及全部动态库的全环境信息,所以在导入 clang Modules 时会失败,原因往往是“编译期没有实现”。

为了应对这种情况,新版本的 LLDB 会从 DWARF 调试信息中导入 C 和 Objective-C 的类型到 Swift 中作为兜底方案。这样也就提高了 Xcode 中 Variable ViewExpression Evaluator 这些与实例对象相关的调试稳定性。

Swift 平台兼容性(Swift Platform Support)

Swift 在官方的发行版也已经支持多个平台,其中有:

  • Apple 各个系统;
  • Ubuntu 16.04, 18.04, 20.04;
  • CentOS 8;
  • Amazon Linux 2;
  • Windows(Coming soon,Swift 5.3)

由于兼容了多个操作系统环境,使得 Swift 将会在更多有趣的场景上投入使用。其中 AWS Lambda 服务就是其中之一。

Serverless Functions(国内通常说是 FasS 功能即服务,或者说是云函数)服务是一种快速落地服务端功能的十分便捷的方式。对于移动端开发,我们可以不用关注很多运维及后端知识。而现在,使用开源的 Swift AWS Runtime 服务来做 Serverless 开发也是个十分简单的方式。

我们通过下面这三行简单的代码,并依托于 Amazon 的 AWS Lambda 服务就可以快速上线一个服务:

语言(Language)

Swift 5.3 又更新了一大批的新特性:

下面我们来分析一些比较有代表性的 Feature,来细说一下:

SE-0279: 多尾部闭包 (Multiple trailing closures)

top Created with Sketch.