vlambda博客
学习文章列表

Xcode 12 beta 中的 Swift 新特性概览

Xcode 12 中集成了 Swift 5.3,并引入了许多新特性,包括语言层面、 Xcode 对 Swift 的支持、Swift Packages 包管理。WWDC 20 也有相应的 Session 来介绍这些内容,如 What's New in Swift 等。这里我们主要整理了 Xcode 12 beta Release Notes (请参考原文)中 Swift 的新特性及解决的问题和 Swift Packages 的内容,另外截取了一些 What's New in Swift 这个 session 中的几个画面,以让大家了解一下 Swift 5.3 的改进。



Swift

新特性

• Swift 的缩进已得到全面改进,大大改善了链式方法调用的缩进,特别是那些涉及嵌套或尾随闭包的调用。

• 改进了某些类型的需求失败时的错误消息。具体来说,这可以改善使用 SwiftUI 的 List 初始化程序时的错误消息,该初始化程序采用可识别数据的集合,且其元素类型不符合 Identifiable 。例如,以下代码:

import SwiftUI

struct NotIdentifiable {}

let data: [NotIdentifiable] = []

List(data) { _ in
Text("Row")
}

将产生以下错误信息:

error: initializer 'init(_:rowContent:)' requires 'NotIdentifiable' conform to 'Identifiable'
List(data) { _ in
^

• Swift缩进现在使guard和if语句中的条件按列对齐:

guard let x = someOptional,
let y = anotherOptional else {
// ...
}

• 现在可以诊断出在词典访问期间计算默认参数的代码中的排他性违规。

struct Container {
static let defaultKey = 0

var dictionary = [defaultKey:0]

mutating func incrementValue(at key: Int) {
dictionary[key, default: dictionary[Container.defaultKey]!] += 1
}
}
// error: overlapping accesses to 'self.dictionary', but modification requires exclusive
// access; consider copying to a local variable
// dictionary[key, default: dictionary[Container.defaultKey]!] += 1
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// note: conflicting access is here
// dictionary[key, default: dictionary[Container.defaultKey]!] += 1
// ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

可以通过使用局部变量预先计算默认参数来避免排他性冲突。

struct Container {
static let defaultKey = 0

var dictionary = [defaultKey:0]

mutating func incrementValue(at key: Int) {
let defaultValue = dictionary[Container.defaultKey]!
dictionary[key, default: defaultValue] += 1
}
}
// No error.

• 现在,当不太可能发生引用循环时,Swift允许在 @escaping 闭包中隐式使用 self 。

首先,如果用户已经在闭包的捕获列表中明确捕获了self,则现在允许在@escaping闭包中隐式使用self,因此以下代码现在有效:

class Test {
var x = 0
func execute(_ work: @escaping () -> Void) {
work()
}
func method() {
execute { [self] in
x += 1
}
}
}

其次,当self是值类型时,隐式self在 @escaping 闭包中可用,使以下代码有效:

struct Test {
var x = 0
func execute(_ work: @escaping () -> Void) {
work()
}
func method() {
execute {
x += 1
}
}
}

• Swift 5.3 引入了 #filePath ,这是一种特殊的字面量,可以生成当前源文件的路径。

在Swift 5.3中, #filePath 和 #file 有相同的行为。但是,Swift的未来版本将更改 #file 以生成更短的,更能保护隐私的字符串,该字符串更适合于二进制文件。对于需要源文件完整路径的测试和其他代码, #filePath 将继续生成一个路径字符串。

我们会尽早使用 #filePath ,以简化将来 #file 行为更改时的过渡。一旦 #file 的行为发生变化,您将需要检查其项目中的任何使用到的地方,尤其是在测试中,以查看他们是否应该使用 #filePath 。但是, #file 的默认行为在 Swift 5.3 中不会更改,因此您无需执行此操作。如果您想尽早更新项目,可以通过在 Xcode 项目的 OTHER_SWIFT_FLAGS 构建设置中添加 -enable-experimental-concise-pound-file 来启用Swift 5.3中的 #file 行为更改。

• 现在,使用默认初始化器时,带有附加属性包装器的属性可以使用类型推断来推断包装的值类型,而默认初始化在包装器属性上没有空括号。例如:

@propertyWrapper
struct IntWrapper {
var wrappedValue: Int { 0 }
}

struct UseWrapper {
@IntWrapper var value
}

包装的属性 UseWrapper.value 使用 IntWrapper 的默认初始化,并依靠类型推断将类型包装的值类型推论为 Int。

• 为了减少增量编译的时间,Swift现在为每种类型(和协议)主体保留单独的指纹。这些类型主体指纹意味着,如果您在还定义了其他结构、枚举、类、协议的文件中更改结构、枚举、类或协议的主体,则仅将更改的实体计为 dirty ,目的是重新编译其他文件。例如,假设您有一个定义两个结构的文件:

struct A {
}
struct B {
}

并在A中添加了一个成员:

struct A {
var x = 17
}
struct B {
}

在进行此更改之前,使用A或B的任何文件都将重新编译。现在,仅使用 B 的文件将不会重新编译。

如果您在使用此功能时遇到问题,请通过 Other Swift Flags 构建设置将 -disable-type-fingerprints 传递到Swift编译器中以将其禁用。如果仍然胡问题,请通过 Other Swift Flags 构建设置将 -disable-fine-grained-dependencies 传递到Swift编译器中,以禁用新的依赖项基础结构。

• 现在,即使在声明本身没有其他通用参数的情况下,通用上下文中的声明现在也可以有where子句:

struct Box<Wrapped> {
func boxes() -> [Box<Wrapped.Element>] where Wrapped: Sequence { ... }
}

• lazy 属性现在可能有 didSet 和/或 willSet 观察者。 

解决的问题

• 现在支持对协议方法的未应用引用。以前,这仅适用于在结构体、枚举和类中定义的方法。

protocol Cat {
func play(catToy: Toy)
}

let fn = Cat.play(catToy:)
fn(myCat)(myToy)

• 当元素对齐时,Swift缩进不再使出现在调用参数,参数列表,数组和字典文字或元组中的多行表达式缩进。

• 实验性函数生成器功能现在已可以正确应用于单表达式闭包。例如:

@_functionBuilder
struct ArrayBuilder<T> {
static func buildBlock(_ values: T…) -> [T] {
values
}
}

func array(@ArrayBuilder<Int> builder: () -> [Int]) -> [Int] {
builder()
}

array { 0 }

这些闭包已经在SwiftUI中起作用了,因为它们只返回一个View,这就是将 ViewBuilder 转换应用于它们的全部效果。但是,并非所有函数构建器转换都具有此属性,现在它们应该可以正常使用。

• 修复了使用属性包装器(其包装的值具有变异的getter和非变异的setter)时发生的编译器崩溃。

• Swift编译器不再在兼容性 header 中为空扩展名或仅具有私有成员且没有公共合规性的扩展名打印 import。

依赖那些可移植导入的 Objective-C/C 源文件可能会引起有关缺少声明的错误,因此需要使用显式导入进行更新以按预期进行编译。

• 修复了由于将使用 objc_runtime_name 的 Objective-C 接口导入 Swift 而导致的崩溃。

• 修复了在子类中使用附加的属性包装器重写属性以实现属性观察器时发生的编译器崩溃。

• 修复了初始化属性包装器可以在初始化所有self之前调用属性观察器的问题。

• 修复了全局重命名可能会更改SDK标头的问题。

Swift Packages

• 现在,您可以为Swift软件包的目标依赖项声明条件,例如按平台限制依赖项。这使您可以更加灵活地描述支持多个平台的复杂目标依赖关系。

• Swift软件包现在可以包含资源,例如图像、资产目录、storyboard 和其他文件。Xcode 构建依赖于程序包的应用程序时,会将程序包的代码和资源添加到应用程序包中,以便在运行时使用。

• Swift软件包现在可以作为 XCFrameworks 分发的预构建库,从而允许依赖于不能作为源代码分发的库。Xcode 构建使用此类软件包的应用程序时,会将库嵌入到应用程序包中。

• Swift软件包现在可以包含软件包中任何资源的本地化内容。除了资产目录中的本地化内容外,Xcode 还支持 .lproj 文件夹中的单独本地化文件。

What's New in Swift

这个 Session 的内容,后续会有小伙伴专门整理,敬请关注。

• Code Size


Xcode 12 beta 中的 Swift 新特性概览

• Heap usage

Xcode 12 beta 中的 Swift 新特性概览

• Code Completion

Xcode 12 beta 中的 Swift 新特性概览

• 支持的平台