동시성 환경에서 data-race를 방지하고 여러 Thread간에 안전하게 값을 전달하기 위한 프로토콜
Sendable을 준수하는 타입은 여러 Thread에서 동시에 접근하더라도 값을 안전하게 전달할 수 있음을 나타냄.
값 타입은 인스턴스를 복사하여 전달되기 때문에 동시성 환경에서 기본적으로 안전하게 사용할 수 있음.
struct Point {
var x: Int // Int는 기본적으로 Sendable을 준수함!
var y: Int
let data = TestData() // 명시적으로 Sendable을 준수하지 않기에 컴파일러가 경고
}
class TestData {
var isData: Bool = false
}
Sendable 프로토콜을 선언하지 않은 TestData 객체를 가진 Point 구조체는 컴파일러가 경고.
⚠️ 주의 사항
class NonSendableClass {
var value: Int = 0
}
actor FirstActor {
let data = NonSendableClass()
func sendData(to actor: SecondActor) async {
await actor.receive(data: data) // 컴파일러 에러 발생
}
}
actor SecondActor {
func receive(data: NonSendableClass) {
// ...
}
}
NSObject를 상속할 것final class SendableClass: Sendable {
private var property1: Int // private이더라도 내부 메서드가 property1을 수정하면?
let property2: Int
...
}
컴파일러가 Sendable 검사를 생략
타입이 실제로는 Thread Safe하나 컴파일러가 잡아낼 경우, 검사를 생략하도록 하는 것
final class MyClass: @unchecked Sendable {
var name: String // 에러를 발생시키지 않음!
}
해당 클로저가 동시성 환경에서 안전하게 실행될 수 있음을 컴파일러에게 알리는 속성
var count = 0
let increment: @Sendable () -> Void = { [count] in
print("Captured count: \(count)")
// count를 값으로 캡처하므로 동시성 환경에서도 안전하게 사용 가능
}
class User {
var name = "Alice"
}
var user = User()
let greet: @Sendable () -> Void = { [user] in
print("Hello, \(user.name)")
// 'user'는 참조 타입이므로 동시성 환경에서 안전하지 않음
}
✨ 해결책
@unchecked Sendable 사용@MainActor 키워드로 정의된 함수는 암시적으로 Sendable로 간주 X, 명시해줘야 함.
MainActor가 Sendable 컴파일러 오류
https://www.hackingwithswift.com/quick-start/concurrency/sending-data-safely-across-actor-boundaries