SE-313 引入了非隔離(nonisolated)和隔離(isolated)關(guān)鍵字作為添加 Actor 隔離控制的一部分邻辉。 Actor 是一種使用新并發(fā)框架為共享可變狀態(tài)提供同步的新方法砌烁。
如果您不熟悉 Swift 中的 Actor,我鼓勵您閱讀我的文章Swift中的Actors 使用以如何及防止數(shù)據(jù)競爭,文章內(nèi)詳細(xì)描述了它技竟。本文將解釋在 Swift 中使用 Actor 時如何控制方法和參數(shù)的隔離疏橄。
了解Actor的默認(rèn)行為
默認(rèn)情況下闰集,actor 的每個方法都是隔離的桨螺,這意味著您必須已經(jīng)在 actor 的上下文中恰梢,或者使用 await 等待批準(zhǔn)訪問 actor 包含的數(shù)據(jù)佛南。
您可以在我的文章 Swift 中的async/await ——代碼實例詳解了解有關(guān) async/await 的更多信息。
通常我們使用Actor會遇到以下錯誤:
- Actor-isolated property ‘balance’ can not be referenced from a non-isolated context
- Expression is ‘a(chǎn)sync’ but is not marked with ‘a(chǎn)wait’
這兩個錯誤都有相同的根本原因:Actor 隔離對其屬性的訪問以確鼻堆裕互斥訪問嗅回。
以如下銀行賬戶 Actor 為例:
actor BankAccountActor {
enum BankError: Error {
case insufficientFunds
}
var balance: Double
init(initialDeposit: Double) {
self.balance = initialDeposit
}
func transfer(amount: Double, to toAccount: BankAccountActor) async throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
await toAccount.deposit(amount: amount)
}
func deposit(amount: Double) {
balance = balance + amount
}
}
Actor 方法默認(rèn)是隔離的,但沒有明確標(biāo)記為隔離摧茴。您可以將此與默認(rèn)情況下為內(nèi)部但未使用 internal
關(guān)鍵字標(biāo)記的方法進(jìn)行比較绵载。實際上真實代碼大概如下所示:
isolated func transfer(amount: Double, to toAccount: BankAccountActor) async throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
await toAccount.deposit(amount: amount)
}
isolated func deposit(amount: Double) {
balance = balance + amount
}
但是,像這個例子一樣使用隔離關(guān)鍵字(isolated)顯式標(biāo)記方法將導(dǎo)致以下錯誤:
‘isolated’ may only be used on ‘parameter’ declarations
我們只能在參數(shù)聲明中使用隔離關(guān)鍵字苛白。
將 Actor 參數(shù)標(biāo)記為隔離
對參數(shù)使用隔離關(guān)鍵字可以很好地使用更少的代碼來解決特定問題娃豹。上面的代碼示例介紹了一個deposit
方法來更改另一個銀行賬戶的余額:
func transfer(amount: Double, to toAccount: isolated BankAccountActor) async throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
toAccount.balance += amount
}
結(jié)果是使用更少的代碼同時可能使您的代碼更易于閱讀。
編譯器目前禁止但允許使用多個隔離參數(shù):
func transfer(amount: Double, from fromAccount: isolated BankAccountActor, to toAccount: isolated BankAccountActor) async throws {
// ..
}
不過购裙,最初的提議表明這是不允許的懂版,因此未來的 Swift 版本可能會要求您更新此代碼。
在 Actor 中使用 nonisolated 關(guān)鍵字
將方法或?qū)傩詷?biāo)記為非隔離可用于選擇退出Actor的默認(rèn)隔離躏率。在訪問不可變值或符合協(xié)議要求時躯畴,選擇退出可能會有所幫助。
在以下示例中禾锤,我們?yōu)锳ctor添加了一個帳戶持有人姓名:
actor BankAccountActor {
let accountHolder: String
// ...
}
帳戶持有人是不可變的私股,因此可以安全地從非隔離環(huán)境訪問。編譯器足夠聰明恩掷,可以識別這種狀態(tài)倡鲸,因此無需顯式將此參數(shù)標(biāo)記為非隔離。
但是黄娘,如果我們引入計算屬性訪問不可變屬性峭状,我們必須幫助編譯器識別這一點克滴。讓我們看一下下面的例子:
actor BankAccountActor {
let accountHolder: String
let bank: String
var details: String {
"Bank: \(bank) - Account holder: \(accountHolder)"
}
// ...
}
如果我們現(xiàn)在要打印出detail
,我們會遇到以下錯誤:
Actor-isolated property ‘details’ can not be referenced from a non-isolated context
bank
和 accountHolder
都是不可變屬性优床,因此我們可以顯式地將計算屬性標(biāo)記為nonisolated
然后便可以解決錯誤:
actor BankAccountActor {
let accountHolder: String
let bank: String
nonisolated var details: String {
"Bank: \(bank) - Account holder: \(accountHolder)"
}
// ...
}
使用非隔離解決協(xié)議一致性
同樣的原則也適用于添加協(xié)議一致性极谊,在這種一致性中代承,您確定只能訪問不可變狀態(tài)召锈。例如携悯,我們可以用更好的 CustomStringConvertible
協(xié)議替換 details
屬性:
extension BankAccountActor: CustomStringConvertible {
var description: String {
"Bank: \(bank) - Account holder: \(accountHolder)"
}
}
使用 Xcode 推薦的默認(rèn)實現(xiàn),我們會遇到以下錯誤:
Actor-isolated property ‘description’ cannot be used to satisfy a protocol requirement
我們可以再次通過使用 nonisolated
關(guān)鍵字解決這個問題:
extension BankAccountActor: CustomStringConvertible {
nonisolated var description: String {
"Bank: \(bank) - Account holder: \(accountHolder)"
}
}
如果我們在非隔離環(huán)境中意外訪問了隔離屬性移层,編譯器將足夠聰明地警告我們:
從非隔離環(huán)境訪問隔離屬性將導(dǎo)致編譯器錯誤。
繼續(xù)您的 Swift 并發(fā)之旅
并發(fā)更改不僅僅是 async-await观话,還包括許多您可以在代碼中受益的新功能予借。所以當(dāng)你在做的時候,為什么不深入研究其他并發(fā)特性呢频蛔?
- Swift 中的 async/await
- Swift 中的 async let
- Swift 中的 Task
- Swift 中的 Actors 使用以如何及防止數(shù)據(jù)競爭
- Swift 中的 MainActor 使用和主線程調(diào)度
- 理解 Swift Actor 隔離關(guān)鍵字:nonisolated 和 isolated
- Swift 中的 Sendable 和 @Sendable 閉包
- Swift 中的 AsyncThrowingStream 和 AsyncStream
- Swift 中的 AsyncSequence
結(jié)論
Swift 中的 Actor 是同步訪問共享可變狀態(tài)的好方法灵迫。然而,在某些情況下晦溪,我們希望控 Actor 隔離瀑粥,因為我們可能確定只訪問不可變狀態(tài)。通過使用非隔離(nonisolated
)和隔離(isolated
)關(guān)鍵字三圆,我們可以精確控制Actor的隔離狀態(tài)利凑。
轉(zhuǎn)自 Nonisolated and isolated keywords: Understanding Actor isolation