
Swift의 동시성(
actors, async/await)에서 값이 어느 격리 영역(region)에 속해 있는지 추적해서, 서로 다른 격리 영역 간에 안전하지 않은 공유/전달이 있는지 검사하는 기능
Sendable을 준수하지 않으면 객체를 다른 Task나 actor로 이동 불가
엄격한 관리로 개발자 입장에서 까다로운 코드 작성
객체가 생성된 영역에서 벗어나 다른 영역으로 전달 된 후, 기존 영역에서 다시 사용되지 않음이 보장되면 Sendable을 준수하지 않더라도 전달 가능
struct ClientManager {
let service: ClientProcessingService
func startConfiguration() async {
let newClient = NonSendableClient(name: "NewUser")
// 'newClient'는 NonSendable이지만, 아래 Task로 전달된 후
// 생성된 영역에서 다시 사용되지 않으므로 안전하다고 판단합니다.
await Task {
await service.processRemote(client: newClient)
}.value
// 만약 여기서 print(newClient.name)을 호출하면 컴파일 에러가 발생
// Task로 전달된 객체를 다시 사용할 수 X
}
}
func example1() {
let a = NonSendable() // region R1 = { a }
let b = a // b는 a와 alias 가능 → R1 = { a, b }
var c = NonSendable() // region R2 = { c }
c = b // 이제 c도 a/b와 같은 인스턴스를 가리킬 수 있음
// → R1 = { a, b, c }, R2는 사라지고 R1로 병합
}
컴파일러는 함수 안의 값들에 격리 정보를 태그처럼 붙이고, 전달 관계를 따라 서로 얽힌 값들을 같은 region으로 그룹화
그룹화 진행 중에 region이 2개 이상의 isolation-domain에 연결되면 동시성 문제 가능성으로 판단하고 에러 발생
@MainActor
final class ViewModel {
var ns: NonSendable?
}
final class NonSendable {
var value = 0
}
func overlapExample(vm: ViewModel) async {
// 메인 actor에서 NonSendable을 만든다고 가정
vm.ns = NonSendable()
// 여기서 vm.ns가 속한 region은 @MainActor에 붙어 있음
// Regions (개념상): [{(vm, vm.ns), @MainActor}]
// 다른 task에서 이 값을 잡아감
Task {
// 이 클로저는 기본적으로 task‑isolated
let local = vm.ns // 메인 actor 값이 task 쪽으로 흘러옴
local?.value += 1
}
}
vm.ns가 main Actor domain과 task domain에서 동시에 접근되면 vm.ns가 속한 region이 두 domain에 있게 됨.
컴파일러가 2개 이상의 domain에 같은 region이 있음을 판단하고 에러
컴파일러가 각 객체의 동시성을 추적함으로써 컴파일 시간에 동시성 문제 조기 발견 가능
기존 안전한 코드에서 생기던 에러를 줄일 수 있음. (컴파일러가 더욱 코드 흐름을 이해하고 있기 때문)
참고
https://github.com/swiftlang/swift-evolution/blob/main/proposals/0414-region-based-isolation.md
https://priorart.substack.com/p/swifts-region-based-isolation-log