지난번 WWDC를 통해 Actor에 대해 알아보았는데 영상만 봐서는 이해를 했다? 의 느낌은 부족하여 문서를 더 읽어보고 하나씩 테스트 해보면서 공부를 하려고한다.
import UIKit
actor BankAccount {
let accountNumber: Int
var balance: Double
init(accountNumber: Int, initialDeposit: Double) {
self.accountNumber = accountNumber
self.balance = initialDeposit
}
}
extension BankAccount {
enum BankError: Error {
case insufficientFunds
}
func transfer(amount: Double, to other: BankAccount) throws {
if amount > balance {
throw BankError.insufficientFunds
}
print("Transferring \(amount) from \(accountNumber) to \(other.accountNumber)")
balance = balance - amount
other.balance = other.balance + amount
// error: actor-isolated property 'balance' can only be referenced on 'self'
}
}
위의 코드는 계좌 Actor고 해당 transfer 함수를통해 계좌이체를 진행한다 라고 리해하면된다
하지만 위의 함수는 문제가 생기는데 외부의 함수에서 Actor의 내부에 함수의 값을 변경하려면 생기는 이슈이다 (actor의 내부 값은 내부에서만 변환가능)
extension BankAccount {
enum BankError: Error {
case insufficientFunds
}
func transfer(amount: Double, to other: BankAccount) async throws {
if amount > balance {
throw BankError.insufficientFunds
}
print("Transferring \(amount) from \(accountNumber) to \(other.accountNumber)")
balance = balance - amount
await other.deposit(amount: balance)
//
// other.balance = other.balance + amount // error: actor-isolated property 'balance' can only be referenced on 'self'
}
}
extension BankAccount {
func deposit(amount: Double) {
assert(amount >= 0)
balance = balance + amount
}
}
그렇기에 deposit 함수를 만들어 내부변수를 변경가능하도록 처리한다
하지만 여기서 특이한점이 있다 왜? deposit의 함수에는 async 비동기 키워드가없는데 실제 사용할때는 await의 키워드를 "꼭"
사용해야만하는가? 여기서 Actor의 특징이 나타나게 되는데 액터내부의 함수는 비동기로도 처리가 될수도있기에(데이터 레이스 방지를 위하여) 사용하는 쪽에서 await키워드를 사용해줘야한다
그래서 실제 사용할때 await키워드가 추가되었고 transfer 마찬가지로 async 키워드가 추가된걸 확인할수있다.
extension BankAccount {
func passGo() {
self.deposit(amount: 200.0) // synchronous is okay because `self` is isolated
}
}
하지만 해당 함수를 보면 또 이야기가 달라진다 actor 내부의 해당함수를 사용할때는 따로 await 키워드가 사용이 되지않는다
동기 액터 함수는 액터의 에서 동기적으로 호출될 수 있지만 self, 이 메서드에 대한 교차 액터 참조에는 비동기 호출이 필요합니다. transfer(amount:to:)함수는 비동기적으로( 에서) 호출하는 other반면, 다음 함수는 passGo동기적으로(암시적 에서) 호출합니다 self.
func checkBalance(account: BankAccount) async {
print(await account.balance) // okay
await account.balance = 1000.0 // error: cross-actor property mutations are not permitted
}
위의 함수는 무엇이 문제냐? 왜 계좌에서 밸런스값을 get하는 것은 비동기적으로 처리하면 가능한데 왜 set은 불가능한가?
외부에서 set을 처리하는 부분은 해당 actor내부에서 처리가되는것이 아닌 외부에서 처리가되기때문에 data race 가 생길수있기때문에 컴파일 단계 자체에서 해당 오류를 체크해준다.
'Swift 공부' 카테고리의 다른 글
[wwdc] Design protocol interfaces in Swift (1) | 2024.11.17 |
---|---|
WWDC2021 Protect mutable state with Swift actors (1) | 2024.09.21 |
WWDC 2021 Meet AsyncSequence 정리 (1) | 2024.09.18 |
WWDC 2021 Explore structured concurrency in Swift 정리 (0) | 2024.09.17 |
WWDC 공부 정리 1차 Meet async/await in Swift (2) | 2024.09.16 |