Swift 5.5 新特性搶先看,async/await 將重磅來(lái)襲
再過(guò)一周的時(shí)間,WWDC21 就正式舉行了,如果不出意外的話(huà),Swift 5.5 測(cè)試版也會(huì)在期間發(fā)布。早在 3 月 13 日,官方論壇就公布了 Swift 5.5 版本的發(fā)布計(jì)劃,并在 4 月 16 日拉出了 release/5.5 分支。經(jīng)過(guò)幾個(gè)月時(shí)間的準(zhǔn)備,從 Swift Evolution 中,我們能發(fā)現(xiàn) Swift 5.5 將為我們帶來(lái)許多期待已久的特性,如 async/await。我們今天就來(lái)簡(jiǎn)單整理一下這些 可能 將在 Swift 5.5 中出現(xiàn)的新特性。
SE-0291 包集合

這個(gè) proposal 旨在為 SwiftPM 添加對(duì)包集合的支持。包集合是包和相關(guān)元數(shù)據(jù)的列表,可以更輕松地發(fā)現(xiàn)特定用例的現(xiàn)有包。SwiftPM 將允許用戶(hù)訂閱這些集合,通過(guò) swift package-collection 命令行界面搜索它們,并使 libSwiftPM 的任何客戶(hù)端都可以訪(fǎng)問(wèn)它們的內(nèi)容。這個(gè) proposal 關(guān)注于以命令行界面和包集合想著的配置數(shù)據(jù)格式。
例如,list 命令將列出用戶(hù)配置的所有集合:
$ swift package-collection list [--json]
My organisation's packages - https://example.com/packages.json
...
describe 命令顯示來(lái)自包本身的元數(shù)據(jù)。
$ swift package-collection describe [--json] https://github.com/jpsim/yams
Description: A sweet and swifty YAML parser built on LibYAML.
Available Versions: 4.0.0, 3.0.0, ...
Watchers: 14
Readme: https://github.com/jpsim/Yams/blob/master/README.md
Authors: @norio-nomura, @jpsim
--------------------------------------------------------------
Latest Version: 4.0.0
Package Name: Yams
Modules: Yams, CYaml
Supported Platforms: iOS, macOS, Linux, tvOS, watchOS
Supported Swift Versions: 5.3, 5.2, 5.1, 5.0
License: MIT
CVEs: ...
https://github.com/apple/swift-evolution/blob/main/proposals/0291-package-collections.md
SE-0293 將屬性包裝器擴(kuò)展到函數(shù)和閉包參數(shù)

Property Wrappers 用于抽象出常見(jiàn)的屬性訪(fǎng)問(wèn)器模式,在 Swift 5.1 中引入。不過(guò)之前僅允許在局部變量和類(lèi)型屬性上應(yīng)用屬性包裝器。而這個(gè) proposal 的目標(biāo)是將屬性包裝器擴(kuò)展到函數(shù)和閉包參數(shù)。
例如,使用來(lái)自 PropertyKit 的驗(yàn)證,我們可以將各種前提條件抽象到一個(gè)屬性包裝器中:
@propertyWrapper
struct Asserted<Value> {
init(
wrappedValue: Value,
validation: Validation<Value>,
) { ... }
var wrappedValue: Value { ... }
}
將 @Asserted 應(yīng)用于參數(shù)以對(duì)參數(shù)值斷言某些先決條件會(huì)很有用。例如,下面的代碼斷言傳遞給 quantity 參數(shù)的參數(shù)大于或等于1:
func buy(
@Asserted(.greaterOrEqual(1)) quantity: Int,
of product: Product,
) { ... }
https://github.com/apple/swift-evolution/blob/main/proposals/0293-extend-property-wrappers-to-function-and-closure-parameters.md
SE-0295 有關(guān)聯(lián)值的枚舉的 Codable 合成

在 SE-0166 中引入了 Codable,它支持合成類(lèi)和結(jié)構(gòu)類(lèi)型的 Encodable 和 Decodable 一致性,其中僅包含也符合各自協(xié)議的值。
這個(gè) proposal 將擴(kuò)展對(duì)枚舉關(guān)聯(lián)值的一致性的自動(dòng)合成的支持。
如以下枚舉有關(guān)聯(lián)值:
enum Command: Codable {
case load(key: String)
case store(key: String, value: Int)
}
將會(huì)被編碼為
{
"load": {
"key": "MyKey"
}
}
{
"store": {
"key": "MyKey",
"value": 42
}
}
編譯器將生成以下 CodingKeys 聲明:
// contains keys for all cases of the enum
enum CodingKeys: CodingKey {
case load
case store
}
// contains keys for all associated values of `case load`
enum LoadCodingKeys: CodingKey {
case key
}
// contains keys for all associated values of `case store`
enum StoreCodingKeys: CodingKey {
case key
case value
}
https://github.com/apple/swift-evolution/blob/main/proposals/0295-codable-synthesis-for-enums-with-associated-values.md
SE-0296 async/await

現(xiàn)代 Swift 開(kāi)發(fā)涉及大量使用閉包和完成處理程序的異步編程,但這些 API 都很難使用。當(dāng)使用許多異步操作、錯(cuò)誤處理或異步調(diào)用之間的控制流變得復(fù)雜時(shí),會(huì)讓問(wèn)題變得很復(fù)雜。我們先看一個(gè)簡(jiǎn)單的例子:
// 一系列簡(jiǎn)單的異步操作通常需要深度嵌套的閉包
func processImageData1(completionBlock: (_ result: Image) -> Void) {
loadWebResource("dataprofile.txt") { dataResource in
loadWebResource("imagedata.dat") { imageResource in
decodeImage(dataResource, imageResource) { imageTmp in
dewarpAndCleanupImage(imageTmp) { imageResult in
completionBlock(imageResult)
}
}
}
}
}
processImageData1 { image in
display(image)
}
而這個(gè)提案則引入了一種語(yǔ)言擴(kuò)展,大大簡(jiǎn)化了這些操作,讓代碼更加自然而不易出錯(cuò)。這種設(shè)計(jì)為 Swift 引入了一個(gè)協(xié)程模型。函數(shù)可以是 async,允許程序員使用正常的控制流機(jī)制編寫(xiě)涉及異步操作的復(fù)雜邏輯。編譯器負(fù)責(zé)將異步函數(shù)轉(zhuǎn)換為一組合適的閉包和狀態(tài)機(jī)。實(shí)際上 async/await 語(yǔ)義早已是現(xiàn)代編程語(yǔ)言的標(biāo)配。我們來(lái)看看,async/await 如何讓代碼變得更加簡(jiǎn)潔:
func loadWebResource(_ path: String) async throws -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
func dewarpAndCleanupImage(_ i : Image) async throws -> Image
func processImageData() async throws -> Image {
let dataResource = try await loadWebResource("dataprofile.txt")
let imageResource = try await loadWebResource("imagedata.dat")
let imageTmp = try await decodeImage(dataResource, imageResource)
let imageResult = try await dewarpAndCleanupImage(imageTmp)
return imageResult
}
不過(guò),這個(gè) proposal 并不提供并發(fā),結(jié)構(gòu)化并發(fā)問(wèn)題由另一個(gè) proposal 引入,它將異步函數(shù)與并發(fā)執(zhí)行的任務(wù)相關(guān)聯(lián),并提供用于創(chuàng)建、查詢(xún)和取消任務(wù)的 API。
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md
SE-0297 與 Objective-C 的并發(fā)互操作性

在 Apple 平臺(tái)上,Swift 與 Objective-C 的混編與交互目前來(lái)講還是一個(gè)很大的課題。在 Objective-C 中,異步 API 隨處可見(jiàn),在 iOS 14.0 SDK 中就包含了近 1000 個(gè)接受 completion 處理器的方法。例如以下 PassKit API 中的方法
- (void)signData:(NSData *)signData
withSecureElementPass:(PKSecureElementPass *)secureElementPass
completion:(void (^)(NSData *signedData, NSData *signature, NSError *error))completion;
這些方法包括可以直接在 Swift 中調(diào)用的方法,可以在 Swift 定義的子類(lèi)中覆蓋的方法,以及可以實(shí)現(xiàn)的協(xié)議中的方法。
當(dāng)前,以上 Objective-C 函數(shù)在 Swift 中會(huì)被翻譯成以下函數(shù):
@objc func sign(_ signData: Data,
using secureElementPass: PKSecureElementPass,
completion: @escaping (Data?, Data?, Error?) -> Void
)
這個(gè) proposal 旨在為 Swift 并發(fā)結(jié)構(gòu)與 Objective-C 之間提供互操作性,并實(shí)現(xiàn)以下目標(biāo):
將 Objective-C 完成處理程序方法轉(zhuǎn)換為 Swift 中的
async方法;允許將 Swift 中定義的
async方法標(biāo)記為@objc,在這種情況下,它們將作為完成處理程序方法導(dǎo)出;提供 Objective-C 屬性來(lái)控制如何將基于完成處理程序的 API 轉(zhuǎn)換為
asyncSwift 函數(shù)。
基于這些假設(shè),以上 Objective-C 函數(shù)將會(huì)被翻譯為以下 async 函數(shù):
@objc func sign(
_ signData: Data,
using secureElementPass: PKSecureElementPass
) async throws -> (Data, Data)
同時(shí)可以通過(guò)以下方式來(lái)調(diào)用:
let (signedValue, signature) = try await passLibrary.sign(signData, using: pass)
https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md
SE-0298 Async/Await: 序列

SE-0296 的 async/await 特性為 Swift 提供了更直觀(guān)的異步編程方式。而這個(gè) proposal 的目標(biāo)是基于 async/await,以?xún)?nèi)置的方式更直觀(guān)地編寫(xiě)和使用隨時(shí)間返回多個(gè)值的函數(shù)。
這個(gè) proposal 主要由三個(gè)部分組成:
表示異步值序列的協(xié)議的標(biāo)準(zhǔn)庫(kù)定義;
編譯器支持在異步值序列上使用 for...in 語(yǔ)法;
對(duì)異步值序列進(jìn)行操作的常用函數(shù)的標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)
基于這個(gè) proposal,迭代異步值序列像迭代同步值序列一樣簡(jiǎn)單。一個(gè)示例用例是迭代文件中的行,如下所示:
for try await line in myFile.lines() {
// Do something with each line
}
為此,標(biāo)準(zhǔn)庫(kù)中定義了以下協(xié)議
public protocol AsyncSequence {
associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element
associatedtype Element
__consuming func makeAsyncIterator() -> AsyncIterator
}
public protocol AsyncIteratorProtocol {
associatedtype Element
mutating func next() async throws -> Element?
}
編譯器生成的代碼將允許在符合 AsyncSequence 的任何類(lèi)型上使用for in循環(huán)。標(biāo)準(zhǔn)庫(kù)還將擴(kuò)展協(xié)議以提供熟悉的通用算法。如以下示例:
struct Counter : AsyncSequence {
let howHigh: Int
struct AsyncIterator : AsyncIteratorProtocol {
let howHigh: Int
var current = 1
mutating func next() async -> Int? {
// We could use the `Task` API to check for cancellation here and return early.
guard current <= howHigh else {
return nil
}
let result = current
current += 1
return result
}
}
func makeAsyncIterator() -> AsyncIterator {
return AsyncIterator(howHigh: howHigh)
}
}
在調(diào)用端,則可以如下使用:
for await i in Counter(howHigh: 3) {
print(i)
}
/*
Prints the following, and finishes the loop:
1
2
3
*/
for await i in Counter(howHigh: 3) {
print(i)
if i == 2 { break }
}
/*
Prints the following:
1
2
*/
https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md
SE-0299 在通用上下文中擴(kuò)展靜態(tài)成員查找

Swift 支持對(duì)具體類(lèi)型的靜態(tài)成員查找,并通過(guò)類(lèi)似枚舉的 . 語(yǔ)法來(lái)調(diào)用。例如,SwiftUI 中使用預(yù)定義的常用值作為靜態(tài)屬性擴(kuò)展了 Font 和 Color 等類(lèi)型。
extension Font {
public static let headline: Font
public static let subheadline: Font
public static let body: Font
...
}
extension Color {
public static let red: Color
public static let green: Color
public static let blue: Color
...
}
可通過(guò)以下方式來(lái)調(diào)用:
VStack {
Text(item.title)
.font(.headline)
.foregroundColor(.primary)
Text(item.subtitle)
.font(.subheadline)
.foregroundColor(.secondary)
}
不過(guò),靜態(tài)成員查找目前存在一個(gè)問(wèn)題:不支持泛型函數(shù)中的協(xié)議成員,所以沒(méi)有辦法使用 . 語(yǔ)法來(lái)調(diào)用,如 SwiftUI 定義了一個(gè) toggleStyle 視圖裝飾器:
extension View {
public func toggleStyle<S: ToggleStyle>(_ style: S) -> some View
}
public protocol ToggleStyle {
associatedtype Body: View
func makeBody(configuration: Configuration) -> Body
}
public struct DefaultToggleStyle: ToggleStyle { ... }
public struct SwitchToggleStyle: ToggleStyle { ... }
public struct CheckboxToggleStyle: ToggleStyle { ... }
目前的調(diào)用方式是,在使用 toggleStyle 修飾符時(shí)將具體類(lèi)型以全名方式寫(xiě)入 ToggleStyle:
Toggle("Wi-Fi", isOn: $isWiFiEnabled)
.toggleStyle(SwitchToggleStyle())
而這個(gè) proposal 的目標(biāo)是放寬對(duì)協(xié)議上訪(fǎng)問(wèn)靜態(tài)成員的限制,讓泛型 API 具有更好的可讀性,即通過(guò)以下方式來(lái)調(diào)用:
Toggle("Wi-Fi", isOn: $isWiFiEnabled)
.toggleStyle(.switch)
https://github.com/apple/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md
SE-0300 異步任務(wù)與同步代碼接口

異步 Swift 代碼需要能夠與使用諸如完成回調(diào)和委托方法之類(lèi)的技術(shù)的現(xiàn)有同步代碼一起工作以響應(yīng)事件。異步任務(wù)可以將自己暫停,然后同步代碼可以捕獲并調(diào)用它們以響應(yīng)事件來(lái)恢復(fù)任務(wù)。
標(biāo)準(zhǔn)庫(kù)將提供 API 來(lái)獲取當(dāng)前異步任務(wù)的延續(xù),這會(huì)掛起任務(wù),并產(chǎn)生一個(gè)值,同步代碼隨后可以通過(guò)該值使用句柄來(lái)恢復(fù)任務(wù)。例如
func beginOperation(completion: (OperationResult) -> Void)
我們可以通過(guò)掛起任務(wù)并在調(diào)用回調(diào)時(shí)使用其連續(xù)性將其恢復(fù)為異步接口,然后將傳遞給回調(diào)的參數(shù)轉(zhuǎn)換為異步函數(shù)的正常返回值:
func operation() async -> OperationResult {
// Suspend the current task, and pass its continuation into a closure
// that executes immediately
return await withUnsafeContinuation { continuation in
// Invoke the synchronous callback-based API...
beginOperation(completion: { result in
// ...and resume the continuation when the callback is invoked
continuation.resume(returning: result)
})
}
}
https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md
SE-0304 結(jié)構(gòu)化并發(fā)

async/await proposal 本向并沒(méi)有引入并發(fā)性:它只是忽略異步函數(shù)中的掛起點(diǎn),它將以與同步函數(shù)基本相同的方式執(zhí)行。而這個(gè) proposal 的目標(biāo)就是在 Swift 中引入結(jié)構(gòu)化并發(fā)的支持,并允許高效實(shí)現(xiàn)的模型來(lái)并發(fā)執(zhí)行異步代碼。
例如以下一段準(zhǔn)備晚餐的代碼:
func chopVegetables() async throws -> [Vegetable] { ... }
func marinateMeat() async -> Meat { ... }
func preheatOven(temperature: Double) async throws -> Oven { ... }
// ...
func makeDinner() async throws -> Meal {
let veggies = try await chopVegetables()
let meat = await marinateMeat()
let oven = try await preheatOven(temperature: 350)
let dish = Dish(ingredients: [veggies, meat])
return try await oven.cook(dish, duration: .hours(3))
}
makeDinner 中的每個(gè)步驟都是異步操作,不過(guò)整個(gè)流程每個(gè)點(diǎn)都會(huì)暫停,直到當(dāng)前步驟完成。而為了更快地準(zhǔn)備好晚餐,我們需要同時(shí)執(zhí)行其中一些步驟,為此,可以將步驟分解為可以并行發(fā)生的不同任務(wù)。而這個(gè) proposal 所提的結(jié)構(gòu)化并發(fā),就可以完成這種任務(wù)。proposal 中的概念很多,在此不詳細(xì)介紹。在使用結(jié)構(gòu)化并發(fā)對(duì)上述代碼改造后,代碼類(lèi)似于以下:
func makeDinner() async throws -> Meal {
// Prepare some variables to receive results from our concurrent child tasks
var veggies: [Vegetable]?
var meat: Meat?
var oven: Oven?
enum CookingStep {
case veggies([Vegetable])
case meat(Meat)
case oven(Oven)
}
// Create a task group to scope the lifetime of our three child tasks
try await withThrowingTaskGroup(of: CookingStep.self) { group in
group.async {
try await .veggies(chopVegetables())
}
group.async {
await .meat(marinateMeat())
}
group.async {
try await .oven(preheatOven(temperature: 350))
}
for try await finishedStep in group {
switch finishedStep {
case .veggies(let v): veggies = v
case .meat(let m): meat = m
case .oven(let o): oven = o
}
}
}
// If execution resumes normally after `withTaskGroup`, then we can assume
// that all child tasks added to the group completed successfully. That means
// we can confidently force-unwrap the variables containing the child task
// results here.
let dish = Dish(ingredients: [veggies!, meat!])
return try await oven!.cook(dish, duration: .hours(3))
}
https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md
SE-0306 Actors

Swift 并發(fā)模型旨在提供一種安全的編程模型,該模型可靜態(tài)檢測(cè)數(shù)據(jù)競(jìng)爭(zhēng)和其他常見(jiàn)的并發(fā)錯(cuò)誤。結(jié)構(gòu)化并發(fā)提議引入了一種定義并發(fā)任務(wù)的方法,并為函數(shù)和閉包提供了數(shù)據(jù)爭(zhēng)用安全性。該模型適用于許多常見(jiàn)的設(shè)計(jì)模式,包括諸如并行映射和并發(fā)回調(diào)模式之類(lèi),但僅限于使用由閉包捕獲的狀態(tài)。
Swift 包含一些類(lèi),這些類(lèi)提供了一種聲明可變狀態(tài)的機(jī)制,這些狀態(tài)可以在程序之間共享。然而,類(lèi)很難在并發(fā)程序中正確使用,需要手動(dòng)同步以避免數(shù)據(jù)競(jìng)爭(zhēng)。我們希望提供使用共享可變狀態(tài)的功能,同時(shí)仍提供對(duì)數(shù)據(jù)競(jìng)爭(zhēng)和其他常見(jiàn)并發(fā)錯(cuò)誤的靜態(tài)檢測(cè)。
actor 模型定義了稱(chēng)為該角色的實(shí)體,非常適合此任務(wù)。Actor 允許聲明并發(fā)域中包含的狀態(tài)包,然后定義對(duì)其執(zhí)行操作的多個(gè)操作。每個(gè) actor 都通過(guò)數(shù)據(jù)隔離來(lái)保護(hù)自己的數(shù)據(jù),從而確保即使在許多客戶(hù)端同時(shí)發(fā)出參與者請(qǐng)求的情況下,在給定的時(shí)間也只有一個(gè)線(xiàn)程可以訪(fǎng)問(wèn)該數(shù)據(jù)。作為 Swift 并發(fā)模型的一部分,actor 提供了與結(jié)構(gòu)化并發(fā)相同的競(jìng)爭(zhēng)和內(nèi)存安全屬性。
actor 是一種引用類(lèi)型,可保護(hù)對(duì)其可變狀態(tài)的訪(fǎng)問(wèn),并隨關(guān)鍵字 actor 一起引入:
actor BankAccount {
let accountNumber: Int
var balance: Double
init(accountNumber: Int, initialDeposit: Double) {
self.accountNumber = accountNumber
self.balance = initialDeposit
}
}
像其他 Swift 類(lèi)型一樣,actor 可以有初始化器,方法,屬性和下標(biāo)。它們可以擴(kuò)展并符合協(xié)議,可以是通用的,也可以與通用一起使用。
主要區(qū)別在于 actor 可以保護(hù)其狀態(tài)免受數(shù)據(jù)爭(zhēng)奪。這是 Swift 編譯器通過(guò)強(qiáng)制使用 actor 及其實(shí)例成員的方式受到一系列限制而靜態(tài)地強(qiáng)制實(shí)施的,統(tǒng)稱(chēng)為 actor 隔離。
https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md
SE-0307 允許互換使用 CGFloat 和 Double 類(lèi)型

Swift 首次發(fā)布時(shí),CGFloat 的類(lèi)型的使用就是一項(xiàng)挑戰(zhàn)。當(dāng)時(shí),大多數(shù) iOS 設(shè)備仍為 32 位。諸如 CoreGraphics 之類(lèi)的 SDK 提供的 API 在 32 位平臺(tái)上采用 32 位浮點(diǎn)值,在 64 位平臺(tái)上采用 64 位值。首次引入這些 API 時(shí),在 32 位平臺(tái)上 32 位標(biāo)量算術(shù)速度更快,但是到 Swift 發(fā)行時(shí),情況已不再如此:直到今天, 64 位標(biāo)量算術(shù)速度與 32 位一樣快。甚至在 32 位平臺(tái)上也是如此。之所以仍然保留了 32/64 位分割,主要是出于源和 ABI 穩(wěn)定性的原因。
而這個(gè) proposal 目標(biāo)是允許 Double 和 CGFloat 類(lèi)型通過(guò)將一種類(lèi)型透明轉(zhuǎn)換為另一種類(lèi)型。
https://github.com/apple/swift-evolution/blob/main/proposals/0307-allow-interchangeable-use-of-double-cgfloat-types.md
SE-0308 #if 支持后綴成員表達(dá)式

Swift 有條件編譯塊 #if ... #endif,它允許根據(jù)一個(gè)或多個(gè)編譯條件的值對(duì)代碼進(jìn)行條件編譯。當(dāng)前,與 C 語(yǔ)言中的 #if 不同的是,每個(gè)子句的主體必須包含完整的語(yǔ)句。但是,在某些情況下,尤其是在結(jié)果生成器上下文中,出現(xiàn)了將 #if 應(yīng)用于部分表達(dá)式的需求。這個(gè)該 proposal 擴(kuò)展了 #if ... #endif以便能夠包圍后綴成員表達(dá)式。
例如當(dāng)前使用的如下代碼:
VStack {
let basicView = Text("something")
#if os(iOS)
basicView
.iOSSpecificModifier()
.commonModifier()
#else
basicView
.commonModifier()
#endif
}
可以改成以下這種方式
VStack {
Text("something")
#if os(iOS)
.iOSSpecificModifier()
#endif
.commonModifier()
}
https://github.com/apple/swift-evolution/blob/main/proposals/0308-postfix-if-config-expressions.md
SE-0310 有效的只讀屬性

異步函數(shù)旨在用于可能會(huì)或始終會(huì)在返回之前暫停執(zhí)行上下文切換的計(jì)算,但缺乏有效的只讀計(jì)算屬性和下標(biāo),因此這個(gè) proposal 升級(jí)了 Swift 的只讀屬性以支持異步并單獨(dú)或一起拋出關(guān)鍵字,從而使它們明顯更靈活。
為了說(shuō)明這一點(diǎn),我們可以創(chuàng)建一個(gè) BundleFile 結(jié)構(gòu)體,嘗試將其內(nèi)容加載到應(yīng)用程序的資源包中。由于文件可能不存在,或者可能存在但由于某種原因而無(wú)法讀取,或者可能可讀但太大,因此需要花費(fèi)一些時(shí)間才能讀取,因此我們可以將 contents 屬性標(biāo)記為異步拋出,如下所示:
enum FileError: Error {
case missing, unreadable
}
struct BundleFile {
let filename: String
var contents: String {
get async throws {
guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else {
throw FileError.missing
}
do {
return try String(contentsOf: url)
} catch {
throw FileError.unreadable
}
}
}
}
因?yàn)?content 既是異步的又是拋出的,所以我們?cè)趪L試讀取它時(shí)必須使用 try await:
func printHighScores() async throws {
let file = BundleFile(filename: "highscores")
try await print(file.contents)
}
https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md
SE-0316 全局 actors

Actor 非常適合隔離實(shí)例數(shù)據(jù),但是當(dāng)需要隔離的數(shù)據(jù)分散在整個(gè)程序中,或者表示程序外部存在的某種狀態(tài)時(shí),將所有代碼和數(shù)據(jù)都放入單個(gè)actor 實(shí)例中可能是不切實(shí)際的(例如,大型程序)甚至是不可能的(與那些假設(shè)無(wú)處不在的系統(tǒng)進(jìn)行交互時(shí))。
global actors 的主要目標(biāo)是將 actor 模型應(yīng)用于只能由主線(xiàn)程訪(fǎng)問(wèn)的狀態(tài)和操作。在應(yīng)用程序中,主線(xiàn)程通常負(fù)責(zé)執(zhí)行主要的事件處理循環(huán),該循環(huán)處理來(lái)自各種來(lái)源的事件并將其傳遞給應(yīng)用程序代碼。global actors 提供了一種機(jī)制,可以利用 actor 的角色來(lái)描述主線(xiàn)程,利用 Swift 的 actor 隔離模型來(lái)幫助正確使用主線(xiàn)程。
@MainActor var globalTextSize: Int
@MainActor func increaseTextSize() {
globalTextSize += 2 // okay:
}
func notOnTheMainActor() async {
globalTextSize = 12 // error: globalTextSize is isolated to MainActor
increaseTextSize() // error: increaseTextSize is isolated to MainActor, cannot call synchronously
await increaseTextSize() // okay: asynchronous call hops over to the main thread and executes there
}
https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md
SE-0317 async let bindings

結(jié)構(gòu)化并發(fā)提供了一個(gè)范式,用于在有范圍的任務(wù)組中生成并發(fā)子任務(wù),建立定義明確的任務(wù)層次結(jié)構(gòu),從而可以透明地處理并發(fā)管理的取消,錯(cuò)誤傳播,優(yōu)先級(jí)管理和其他棘手的細(xì)節(jié)。
這個(gè) proposal 旨在使用類(lèi)似于 let 綁定的輕量級(jí)語(yǔ)法,使生成子任務(wù)的常規(guī)任務(wù)異步運(yùn)行,并將最終結(jié)果傳遞給父任務(wù)。
還是以午餐為例,使用 async let,那么代碼看起來(lái)是下面這種:
func makeDinner() async throws -> Meal {
async let veggies = chopVegetables()
async let meat = marinateMeat()
async let oven = preheatOven(temperature: 350)
let dish = Dish(ingredients: await [try veggies, meat])
return try await oven.cook(dish, duration: .hours(3))
}
https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md
小結(jié)
這里只是整理了部分在 Swift 5.5 中實(shí)現(xiàn)的 proposal 或者是在當(dāng)前 main 快照中審核的 proposal??梢钥吹?async/await 及一些異步模型將會(huì)是 Swift 5.5 的重點(diǎn)內(nèi)容。當(dāng)然,最終 Swift 5.5 會(huì)新增哪些特性,還需要等最后的結(jié)果。也許 Swift 團(tuán)隊(duì)會(huì)將一些新特性放到 Swift 6 中發(fā)布。讓我們期待一下 WWDC21 吧。
? 為 iPad 部署基于 VS Code 的遠(yuǎn)程開(kāi)發(fā)環(huán)境
? “And away we code. ” WWDC21 超 200 個(gè) Session 等著你
? Google 正式發(fā)布 Fuchsia OS,F(xiàn)lutter 集成尚存問(wèn)題
? 京東APP訂單業(yè)務(wù)Swift優(yōu)化總結(jié)
就差您點(diǎn)一下了 ??????
