actor는 한번에 하나의 작업만 데이터에 접근 할 수 있도록 내부 변경 가능 상태에 대한 비동기 접근을 제어하는 참조 타입이다.actor는 속성, 생성자, 메서드를 포함한다는 점에서 클래스와 매우 유사하다.actor 는 class의 큰 차이점을 가지고 있다 그것은 비동기 함수 또는 Task 클로저 내와 같은 비동기 콘텍스트 내에서만 생성 및 액세스 할 수 있다는 것이다. 또한 actor 메서드를 호출 하거나 속성에 접근 할 때 await 키워드를 반드시 사용해야 한다. actor인스턴스에 포함된 데이터는 앱의 다른 코드와 격리된다. 이러한 격리 는 인스턴스 데이터를 변경 하는 메서드가 호출될 때, 다른 곳의 코드에서 해당 메서드를 호출 할 수 있게 되기 전에 메서드가 완전히 실행 되는 것을 보장한다.actor 는 클래스와 다르게 상속을 지원해주지 않는다. 다만 프로토콜을 지원해준다. 수평 확장을 지원해줌 정리하자면 data race(데이터 경쟁)을 피하기 위해 Actor를 사용하는 것이며 Actor는 Shared Mutable State에 대한 동기화 메커니즘이다. 때문에 상태(데이터)에 동시에 접근하지 않도록 해준다.
actor BuildErrorMessage {
var message: String
let statusCode: Int
init(message: String, statusCode: Int) {
self.message = message
self.statusCode = statusCode
}
func setErorr(message: String) {
self.message = message
}
nonisolated func setStatusCode() -> Int {
return statusCode
}
}
actor는 각자의 data를 격리(isolated)라는 개념을 이용해 보호합니다. 이를 통해 한 데이터(자원)에 동시에 단 하나의 스레드만 접근할 수 있도록 해 의도치 않는 data race 를 막을 수 있습니다.actor를 사용하여 data race 를 방지하는 것이다.actor 내에 정의된 stored, computed instance properties, instance methods, instance subscripts는 모두 기본적으로 actor-isolated 상태 이다. actor 내에서만 접근 할 수 있다. 즉 self를 통해서 접근이 가능하다. 다만 immutable 의 상태를 가진 let은 self 키워드를 명시하지 않고 접근이 가능하다. 이것을 Cross-Actor Reference 라고 한다.actor 내부에서 data를 비격리(nonisolated)상태로 만들기 위해서는 nonisolated 키워드를 명시하면 된다. nonisolated 함수에서 만약 actor 내부에 immutable state 에 접근한다면 굳이 await 키워드를 사용하지 않아도 된다. isolated 일 경우 await 키워드를 사용해야 한다.class BuildMessage {
var message: String
let greeting: String
func setName(name: String) {
self.message = "\(greeting) \(name)"
}
init(message: String, greeting: String) {
self.message = message
self.greeting = greeting
}
}
actor BankAccount {
let accountNumber: Int
var balance: Double
let accountName: String
nonisolated var displayName: String {
return self.accountName + "입니다."
}
init(accountNumber: Int, balance: Double, accountName: String) {
self.accountNumber = accountNumber
self.balance = balance
self.accountName = accountName
}
}
extension BankAccount {
enum BankError: Error {
case insufficientFunds
}
func transform(amount: Double, to other: BankAccount) throws {
if amount > self.balance {
throw BankError.insufficientFunds
}
print("Transferring \(amount) from \(accountNumber) to \(other.accountNumber)")
self.balance = balance - amount
print(other.accountNumber)
/* other.balance = other.balance + amount*/ // Actor-isolated property 'balance' can not be mutated on a non-isolated actor instance
}
nonisolated func getAccountNumber() -> Int {
return accountNumber
}