Swift 5.3 预发布特性抢先看

Tibor Bodecs · 2020-05-18
本文来自 知识小集 ,作者 Tibor Bodecs

image.png

作者 | Tibor Bodecs 
来源 | https://theswiftdev.com/

Swift 5.3将是一个令人兴奋的新版本。这篇文章展示了最新的 Swift 特性。

从 Apple 在 3 月下旬公布 Swift 5.3 发布流程至今,5.3 分支上已经实现了许多新功能。如果你想了解一下都有什么新功能,可以使用 swiftenv 安装最新快照来体验一下。

Package Manager 更新

Swift Package tool 5.3 版本引入了一些非常好的功能。

资源

随着 SE-0271 的实现,Swift Package Manager 最终可以将资源文件与代码打包在一起。我相信这是一个非常受欢迎的新功能,因为有些库需要嵌入资产文件,但目前为止 SPM 还无法支持。

本地化资源

SE-0278 扩展了资源支持,通过此实现,可以为 Swift 软件包声明本地化的资源。SE-0278 的描述解释了提议的详细信息,如果需要随包一起发布本地化文件,可以看看。

二进制依赖

另一个很棒的事情是 SPM 最终将能够使用二进制依赖项。SE-0272添加了此功能,因此如果希望提供闭源的人现在可以使用此功能。这样可以在给定的路径或位置设置 binaryTarget 依赖性,并且可以将二进制文件用于库或或执行文件。

条件目标依赖

SE-0273 提供了一个很好的补充,可以使用基于给定平台的依赖项。这意味着在为特定平台构建产品时,可以将产品用于一个 target。

以上功能是对 SPM 的重要补充,希望 Xcode 也能从这些功能中受益,并且我们还将在即将发布的 IDE 版本中看到一些重大的新的增强功能。

语言特性

5.3 版本中引入了许多有趣的新提议。

多个尾随闭包

SE-0279 是争议最大的新提案之一。当我第一次看到它时,我不确定是否需要它,为什么有人会花这么大的力气消除一些括号呢?

import UIKit

class ViewController: UIViewController {

   override func viewDidLoad() {
       super.viewDidLoad()

       // old
       UIView.animate(withDuration: 0.3, animations: {
         self.view.alpha = 0
       }, completion: { _ in
         self.view.removeFromSuperview()
       })
       // still old
       UIView.animate(withDuration: 0.3, animations: {
         self.view.alpha = 0
       }) { _ in
         self.view.removeFromSuperview()
       }

       // new
       UIView.animate(withDuration: 0.3) {
         self.view.alpha = 0
       }
       
       UIView.animate(withDuration: 0.3) {
           self.view.alpha = 0
       } completion: { _ in
           self.view.removeFromSuperview()
       }
   }
}

如你所见,这主要是一种语法糖,但是我说服自己拥有它是很好的。

为枚举类型合成 Comparable

由于 SE-0266,Enum 类型不必显式实现 Comparable 协议。

enum Membership: Comparable {
   case premium(Int)
   case preferred
   case general
}
([.preferred, .premium(1), .general, .premium(0)] as [Membership]).sorted()

Comparable 协议是自动合成的,就像其它类型的 Equatable 和 Hashable 一样。当然,如果需要,您可以提供自己的实现。

枚举的 case 实现协议

Swift 的枚举是功能强大构建基块,现在它们变得更好了。

protocol DecodingError {
 static var fileCorrupted: Self { get }
 static func keyNotFound(_ key: String) -> Self
}

enum JSONDecodingError: DecodingError {
 case fileCorrupted
 case keyNotFound(_ key: String)
}

SE-0280 的主要目标是解除现有限制,如果枚举提供的 case 其命名和参数能符合协议的要求,则枚举可以作为协议的一个实现。

基于类型的程序入口点

SE-0281为我们提供了一个新的 @main 属性,可以使用该属性来定义应用程序的入口点。这是一个很好的补充,不必再编写 MyApp.main() 方法,而只需用main属性标记 MyApp 对象。

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

   static func main() {
       print("App will launch & exit right away.")
   }
}

不推荐使用UIApplicationMain和NSApplicationMain属性,而推荐使用@main,我敢打赌这将在下一个主要版本中发布...

多模式 Catch

SE-0276 是另一种语法糖,非常方便同时捕获多个异常。

do {
   try performTask()
}
catch TaskError.someRecoverableError {
   recover()
}
catch TaskError.someFailure(let msg), TaskError.anotherFailure(let msg) {
   showMessage(msg)
}

这消除了在 catch 块中使用 switch case。

Float16

SE-0277 将 Float16 添加到标准库中。

let f16: Float16 = 3.14

泛型数学函数也即将推出...

Self 的改变

SE-0269 aka。对于那些不太愿意写 self 的人来说,在不太可能发生引用循环时提高@escaping 闭包中隐式 self 的可用性是一个不错的选择。

//old
execute {
   let foo = self.doFirstThing()
   performWork(with: self.bar)
   self.doSecondThing(with: foo)
   self.cleanup()
}

//new
execute { [self] in
   let foo = doFirstThing()
   performWork(with: bar)
   doSecondThing(with: foo)
   cleanup()
}

这将允许我们仅将self写入捕获列表,而稍后在块内将其忽略。

完善 didSet 语义

SE-0268 是一个底层改进,使 didSet 行为更佳,更可靠。

class Foo {
   var bar = 0 {
       didSet { print("didSet called") }
   }

   var baz = 0 {
       didSet { print(oldValue) }
   }
}

let foo = Foo()
// This will not call the getter to fetch the oldValue
foo.bar = 1
// This will call the getter to fetch the oldValue
foo.baz = 2

简而言之,以前总是调用属性的getter,但是从现在开始,仅当我们使用 didSet 块中的 oldValue 参数时,才调用该方法。

在非连续元素上添加集合操作

SE-0270 添加了一个 RangeSet 类型来表示多个不连续的范围,以及用于创建和使用范围集的各种集合操作。

var numbers = Array(1...15)

// Find the indices of all the even numbers
let indicesOfEvens = numbers.subranges(where: { $0.isMultiple(of: 2) })

// Perform an operation with just the even numbers
let sumOfEvens = numbers[indicesOfEvens].reduce(0, +)
// sumOfEvens == 56

// You can gather the even numbers at the beginning
let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex)
// numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]
// numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14]

该建议还通过一些使用RangeSet类型的API方法扩展了Collection类型,如果您经常使用范围,则应该看看。

上下文范型声明的 where子句

如果仅引用泛型参数,则使用 SE-0267 可以实现函数并在其上施加where约束。考虑以下代码段:

protocol P {
   func foo()
}

extension P {
   func foo() where Self: Equatable {
       print("lol")
   }
}

这在老版本上无法编译,但是在 Swift 5.3 之后它将像魔术一样工作。

添加可访问未初始化存储的字符串初始化器

SE-0263 添加了一个新的String初始化器,使您可以使用未初始化的缓冲区。

let myCocoaString = NSString("The quick brown fox jumps over the lazy dog") as CFString
var myString = String(unsafeUninitializedCapacity: CFStringGetMaximumSizeForEncoding(myCocoaString, ...)) { buffer in
   var initializedCount = 0
   CFStringGetBytes(
       myCocoaString,
       buffer,
       ...,
       &initializedCount
   )
   return initializedCount
}
// myString == "The quick brown fox jumps over the lazy dog"

通过使用这种新的init方法,您不必再担心不安全的指针了。

Swift的未来发展

目前,在Swift的开发面板上还有6个被接受的提案,其中一个正在接受审核。Swift 5.3将包含一些社区期待已久的惊人新功能。我很高兴该语言朝着正确的方向发展。