Swift 5.5 introduced a new reference type called actor that protects access to its mutable state, and is declared with the keyword actor
. Actor instances can be freely shared across concurrently-executing code, and the actor itself will internally maintain synchronization.
actor BankAccount {
let accountNumber: Int
var balance: Double
init(accountNumber: Int, initialDeposit: Double) {
self.accountNumber = accountNumber
self.balance = initialDeposit
}
}
Actors can have initializers, methods, properties, and subscripts. They can be extended and conform to protocols, be generic, and be used with generics.
The ability to protect their state from data races1 (but not necessarily free from race conditions2) is enforced statically by the Swift compiler through a set of limitations on the way in which actors and their instance members can be used, collectively called actor isolation.
self
.extension BankAccount {
func deposit(amount: Double) {
assert(amount >= 0)
balance = balance + amount
}
}
func checkBalance(account: BankAccount) async {
print(await account.balance) // okay
await account.balance = 1000.0 // error: cross-actor property mutations are not permitted
}
Compile-time actor-isolation checking determines which references to actor-isolated declarations are cross-actor references, and ensures that such references use one of the two permissible mechanisms described above. This ensures that code outside of the actor does not interfere with the actor’s mutable state.