[译]Swift 5.3有什么新功能?–使用Swift进行黑客攻击

145 阅读9分钟

原文链接:What’s new in Swift 5.3? – Hacking with Swift

多个尾随闭包,大规模的包管理器改进等等。

Swift 5.3为Swift带来了另一系列改进,包括一些强大的新功能,如多模式捕获子句和多个尾随闭包,以及Swift Package Manager的一些重要更改。

在本文中,我将介绍每一个主要的变化,同时提供实践代码示例,以便您可以自己尝试。我鼓励您通过Swift Evolution提案的链接了解更多信息,如果您错过了我之前在**Swift 5.2文章中的**新内容,请也查看一下。

赞助与Swift黑客,并达到世界上最大的Swift社区!

多模式捕获条款

**SE-0276**引入了在单个捕获块内捕获多个错误案例的能力,这允许我们在错误处理中删除一些重复。

例如,我们可能有一些代码为一个错误定义了两个枚举情况:

enum TemperatureError: Error {    case tooCold, tooHot}

当读取某物的温度时,我们可以抛出其中一个错误,或者发回“确定”:

func getReactorTemperature() -> Int {    90}func checkReactorOperational() throws -> String {    let temp = getReactorTemperature()    if temp < 10 {        throw TemperatureError.tooCold    } else if temp > 90 {        throw TemperatureError.tooHot    } else {        return "OK"    }}

当要捕捉抛出的错误时,SE-0276让我们用逗号分隔它们,以同样的方式处理太热和太冷:

do {    let result = try checkReactorOperational()    print("Result: \(result)")} catch TemperatureError.tooHot, TemperatureError.tooCold {    print("Shut down the reactor!")} catch {    print("An unknown error occurred.")}

您可以根据需要处理任意多的错误案例,如果需要,您甚至可以从错误中绑定值。

多个尾随闭包

**SE-0279**引入了多个尾随闭包,从而提供了一种更简单的方法来调用具有多个闭包的函数。

这在Swift UI中尤其受欢迎,其中代码如下:

struct OldContentView: View {    @State private var showOptions = false    var body: some View {        Button(action: {            self.showOptions.toggle()        }) {            Image(systemName: "gear")        }    }}

现在可以这样写:

struct NewContentView: View {    @State private var showOptions = false    var body: some View {        Button {            self.showOptions.toggle()        } label: {            Image(systemName: "gear")        }    }}

从技术上讲,标签:不需要和前面的}在同一行,所以如果你愿意,你甚至可以写这个:

struct BadContentView: View {    @State private var showOptions = false    var body: some View {        Button {            self.showOptions.toggle()        }        label: {            Image(systemName: "gear")        }    }}

然而,为了可读性,我要警告不要这样做——像这样的浮动代码永远不会令人愉快,在Swift中,它看起来像一个标记的块,而不是按钮初始化器的第二个参数。

**注意:**在Swift论坛上有很多关于多次跟踪关闭的热烈讨论,我想借此机会提醒人们在参与我们的社区时要文明。像这样值得注意的语法变化一开始总是很奇怪,但是请给它时间,看看你在实践中进展如何。

枚举的综合可比一致性

**SE-0266**允许我们为没有关联值或关联值本身是可比的枚举选择可比一致性。这允许我们使用<、>和相似来比较同一个枚举中的两个情况。

例如,如果我们有一个描述服装尺寸的枚举,我们可以让斯威夫特合成这样的可比一致性:

enum Size: Comparable {    case small    case medium    case large    case extraLarge}

我们现在可以创建该枚举的两个实例,并使用<进行比较,如下所示:

let shirtSize = Size.smalllet personSize = Size.largeif shirtSize < personSize {    print("That shirt is too small")}

这种综合的一致性对于可比的相关值非常有效。例如,如果我们有一个枚举描述了一个球队赢得足球世界杯,我们可以这样写:

enum WorldCupResult: Comparable {    case neverWon    case winner(stars: Int)}

然后,我们可以创建具有不同值的枚举的几个实例,并让Swift对它们进行排序:

let americanMen = WorldCupResult.neverWonlet americanWomen = WorldCupResult.winner(stars: 4)let japaneseMen = WorldCupResult.neverWonlet japaneseWomen = WorldCupResult.winner(stars: 1)let teams = [americanMen, americanWomen, japaneseMen, japaneseWomen]let sortedByWins = teams.sorted()print(sortedByWins)

这将对排列进行排序,以便两个没有赢得世界杯的球队排在第一位,然后是日本女队,然后是美国女队——它认为两个胜利者的情况高于两个从未赢过的情况,并认为胜利者(星星: 4)高于胜利者(星星: 1)。

自我在许多地方不再需要

**SE-0269**允许我们在许多不必要的地方停止使用自己。在这个变化之前,我们需要写自我。在任何引用自我的闭包中,所以我们将捕获语义学显式化,但是我们的闭包通常不能导致引用循环,这意味着自我只是杂乱的。

例如,在此更改之前,我们会编写如下代码:

struct OldContentView: View {    var body: some View {        List(1..<5) { number in            self.cell(for: number)        }    }    func cell(for number: Int) -> some View {        Text("Cell \(number)")    }}

对self.cell(for:)的调用不能导致引用周期,因为它是在结构中使用的。多亏了SE-0269,我们现在可以像这样编写相同的代码:

struct NewContentView: View {    var body: some View {        List(1..<5) { number in            cell(for: number)        }    }    func cell(for number: Int) -> some View {        Text("Cell \(number)")    }}

这可能在任何大量使用闭包的框架中非常流行,包括Swift UI和合并。

基于类型的程序入口点

**SE-0281**引入了一个新的@main属性,允许我们声明程序的入口点在哪里。这允许我们准确地控制代码的哪一部分应该开始运行,这对命令行程序特别有用。

例如,以前创建终端应用时,我们需要创建一个名为main.swift的文件,该文件能够引导我们的代码:

struct OldApp {    func run() {        print("Running!")    }}let app = OldApp()app.run()

Swift自动将main.swift中的代码视为顶级代码,因此它将创建应用实例并运行它。即使在SE-0281之后,情况仍然如此,但是现在如果您愿意,您可以删除main.swift,而是使用@main属性来标记包含静态main()方法的结构或基类,该方法将用作程序的入口点:

@mainstruct NewApp {    static func main() {        print("Running!")    }}

运行时,Swift将自动调用NewApp.main()来启动代码。

UI Kit和App Kit开发人员将熟悉新的@main属性,我们使用@UI Application Main和@NS Application Main来标记我们的应用委托。

然而,在使用@main时,您应该注意一些附带条件:

  • 不能在已经有main.swift文件的应用中使用此属性。

  • 你不能有一个以上的@main属性

  • @main属性只能应用于基类–它会被任何子类继承。

上下文泛型声明的子句

**SE-0267**引入了在泛型类型和扩展中向函数附加here子句的功能。

例如,我们可以从一个简单的Stack结构开始,它允许我们从私有数组中推送和弹出值:

struct Stack<Element> {    private var array = [Element]()    mutating func push(_ obj: Element) {        array.append(obj)    }    mutating func pop() -> Element? {        array.popLast()    }}

使用SE-0267,我们可以向该堆栈添加一个新的排序()方法,但仅限于堆栈中的元素符合可比:

extension Stack {    func sorted() -> [Element] where Element: Comparable {        array.sorted()    }}

列举案件作为协议证人

SE-0280允许枚举参与协议见证匹配,这是一种技术方式,表示它们现在可以更容易地匹配协议的要求。

例如,您可以编写代码来处理各种类型的数据,但是如果这些数据丢失了呢?当然,您可以使用类似nil coalesc ing的东西来每次提供默认值,但是您也可以创建一个需要默认值的协议,然后使各种类型符合您想要的默认值:

protocol Defaultable {    static var defaultValue: Self { get }}extension Int: Defaultable {    static var defaultValue: Int { 0 }}extension Array: Defaultable {    static var defaultValue: Array { [] }}extension Dictionary: Defaultable {    static var defaultValue: Dictionary { [:] }}

SE-0280允许我们做的与枚举完全相同。例如,您想创建一个填充枚举,它可以采用一定数量的像素、一定数量的厘米或系统决定的默认值:

enum Padding: Defaultable {    case pixels(Int)    case cm(Int)    case defaultValue}

在SE-0280之前,这种代码是不可能的——斯威夫特会说填充不满足协议。然而,如果你认为它通过协议真的很满意:我们说它需要一个返回Self的静态默认值,即任何符合协议的具体类型,这正Padding. default Value所做的。

精化didSet语义

SE-0268调整didSet属性观察员的工作方式,使其更有效。这不需要代码更改,除非您以某种方式依赖于以前的错误行为;你只会免费得到一个小小的性能改进。

在内部,这种变化使得Swift在您没有使用旧值的任何情况下设置新值时都不会检索以前的值,如果您没有引用old Value并且没有意愿Set Swift将就地更改您的数据。

如果你碰巧依赖旧的行为,你可以通过引用old Value来触发你的自定义getter来解决它,就像这样:

didSet {    _ = oldValue}

一个新的Float 16类型

SE-0277引入了一种新的半精度浮点类型,称为Float 16,它通常用于图形编程和机器学习。

这种新的浮点类型与Swift的其他类似类型一样:

let first: Float16 = 5let second: Float32 = 11let third: Float64 = 7let fourth: Float80 = 13

Swift Package Manager可获得二进制依赖项、资源等

Swift 5.3为Swift Package Manager(SPM)引入了许多改进。虽然不可能在这里给出这些例子,但我们至少可以讨论发生了什么变化以及原因。

首先,SE-0271(Package Manager Resources)允许SPM包含诸如图像、音频、JSON等资源。这不仅仅是将文件复制到一个完成的应用程序包中——例如,我们可以对我们的资产应用定制处理步骤,比如为iOS优化图像。这还添加了一个新的Bundle.module属性,用于在运行时访问这些资产。SE-0278(包管理器本地化资源)在此基础上构建,以允许本地化版本的资源,例如法语图像。

其次,SE-0272(Package Manager二进制依赖)允许SPM使用二进制包以及它对源代码包的现有支持。这意味着现在可以使用SPM集成常见的闭源SDK,如Firebase。

第三,SE-0273(Package Manager条件目标依赖项)允许我们将目标配置为仅具有特定平台和配置的依赖项。例如,我们可能会说,在为Linux编译时,我们需要一些特定的额外框架,或者在为本地测试编译时,我们应该构建一些调试代码。

值得补充的是,SE-0271的“未来方向”部分提到了对单个资源文件的类型安全访问的可能性——SPM能够为我们的资源文件生成特定的声明作为Swift代码,这意味着像图像(“化身”)这样的东西变成了Image(module.avatar)。

下一步在哪里?

我们希望在WWDC 20看到Swift 5.3的第一个测试版与Xcode Next一起发布,但与此同时,您可以从Swift.org.下载夜间工具链快照

我还建议您查看Swift标准库预览版——这篇文章的早期版本以SE-0270为特色,它在不连续的元素上添加了新的收集方法,但随后被移至库预览版。所以,快去试一试吧,看看你是怎么想的!

赞助与Swift黑客,并达到世界上最大的Swift社区!