How to make parts of an actor nonisolated
How to make parts of an actor nonisolated 관련
Updated for Xcode 15
All methods and mutable properties inside an actor are isolated to that actor by default, which means they cannot be accessed directly from code that’s external to the actor. Access to constant properties is automatically allowed because they are inherently safe from race conditions, but if you want you can make some methods excepted by using the nonisolated
keyword.
Actor methods that are non-isolated can access other non-isolated state, such as constant properties or other methods that are marked non-isolated. However, they cannot directly access isolated state like an isolated actor method would; they need to use await
instead.
To demonstrate non-isolated methods, we could write a User
actor that has three properties: two constant strings for their username and password, and a variable Boolean to track whether they are online. Because password
is constant, we could write a non-isolated method that returns the hash of that password using CryptoKit, like this:
import CryptoKit
import Foundation
actor User {
let username: String
let password: String
var isOnline = false
init(username: String, password: String) {
self.username = username
self.password = password
}
nonisolated func passwordHash() -> String {
let passwordData = Data(password.utf8)
let hash = SHA256.hash(data: passwordData)
return hash.compactMap { String(format: "%02x", $0) }.joined()
}
}
let user = User(username: "twostraws", password: "s3kr1t")
print(user.passwordHash())
I’d like to pick out a handful of things in that code:
- Marking
passwordHash()
asnonisolated
means that we can call it externally without usingawait
. - We can also use
nonisolated
with computed properties, which in the previous example would have madenonisolated var passwordHash: String
. Stored properties may not be non-isolated. - Non-isolated properties and methods can access only other non-isolated properties and methods, which in our case is a constant property. Swift will not let you ignore this rule.
Non-isolated methods are particularly useful when dealing with protocol conformances such as Hashable
and Codable
, where we must implement methods to be run from outside the actor.
For example, if we wanted to make our User
actor conform to Codable
, we’d need to implement encode(to:)
ourselves as a non-isolated method like this:
actor User: Codable {
enum CodingKeys: CodingKey {
case username, password
}
let username: String
let password: String
var isOnline = false
init(username: String, password: String) {
self.username = username
self.password = password
}
nonisolated func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(username, forKey: .username)
try container.encode(password, forKey: .password)
}
}
let user = User(username: "twostraws", password: "s3kr1t")
if let encoded = try? JSONEncoder().encode(user) {
let json = String(decoding: encoded, as: UTF8.self)
print(json)
}