Sendable
임의의 동시성 컨텍스트 사이에서 data race 위험 없이 값을 안전하게 공유할 수 있는 thread-safe한 타입
Overview
이 타입의 값은
- 공유 가변 데이터가 없거나
- 가변 데이터가 락에 의해 보호되거나
- 특정
actor에서만 접근이 가능하도록 제한 함으로써 보호된다.
Sendable 타입의 값은 하나의 동시성 도메인에서 다른 곳으로 안전하게 전달될 수 있다.
예를들어, actor의 메서드를 호출할 때 매개변수로 Sendable 값을 전달할 수 있다.
다음에 해당되는 데이터를 Sendable로 표시할 수 있다.
- 값 타입
- 가변 상태(mutable state)가 없는 참조 타입
- 자신의 상태에 대한 접근을 내부적으로 관리하는 참조타입
- 함수와 클로저(
@Sendable로 표시한 경우)
Sendable 프로토콜은 강제하는 메서드나 프로퍼티가 없지만, 컴파일 타임에 강제되는 개념적인 요구사항이 있다.
Sendable 채택은 타입 선언과 같은 파일에서 이루어져야 한다.
→ 즉, extension에서 채택하는 것이 아니라 선언과 동시에 이루어져야 한다.
struct User {
...
}
extension User: Sendable {} // ❌Sendable 타입 채택 시 컴파일러가 강제하는 요구사항을 무시하려면, @unchecked Sendable을 사용한다.
uncheck sendable 타입에 대한 책임은 개발자에게 있다. 예를들어 상태에 대한 접근을 락이나 큐를 통해 보호해야한다.
unchecked Sendable로 선언하게 되면, 동일한 파일 내에서 선언해야 한다는 컴파일러의 규칙이 적용되지 않는다.
Sendable 구조체와 enum
열거형(enumeration)과 구조체에서 Sendable 프로토콜의 요구사항을 만족하려면, 모든 저장 프로퍼티나 연관 값이 Sendable이어야 한다.
어떤 경우에서는 열거형 또는 구조체가 Sendable의 아래 요구사항을 만족하면 명시적으로 Sendable을 채택하지 않아도 Sendable을 채택하게 된다.
@frozen으로 선언된 구조체와 열거형public으로 선언되지 않았으며,@usableFromInline으로 선언되지 않은 구조체와 열거형
그 외의 경우에는 명시적으로 Sendable을 채택해야 한다.
Sendable이 아닌 저장 프로퍼티를 가진 구조체나, Sendable이 아닌 연관값을 가진 열거형은 Sendable 프로토콜이 요구하는 조건을 개발자가 직접 관리하여 컴파일 타임에 확인을 비활성화 할 수 있는 @uncheckable Sendable로 선언할 수 있다.
Sendable Actor
모든 액터 타입은 Sendable을 만족해야 한다. Actor는 모든 가변 상태에 대한 접근을 직렬화하여 수행하도록 보장하기 때문이다.
Sendable Classes
클래스에서 Sendable을 만족하기 위해서 요구되는 사항들
final키워드 붙이기- immutable하면서
sendable한 저장프로퍼티만 포함 - superclass가 없거나
NSObject를 superclass로 가지는 경우- → 아무 클래스도 상속하지 않거나,
NSObject만 상속하는 경우
- → 아무 클래스도 상속하지 않거나,
@MainActor가 적용된 클래스는 상태에 대한 모든 접근이 MainActor에 의해 직렬화되기 때문에 별도 선언 없이 Sendable을 준수하게 된다. 이러한 클래스는 mutable하고 Sendable이 아닌 저장 프로퍼티를 가질 수 있다.
위 요구사항을 충족하지 않는 클래스는 개발자가 직접 Sendable 프로토콜의 요구사항을 확인한 후, 컴파일 타임 체크를 비활성화 하는 @unckecked Sendable로 표시할 수 있다.
Sendable 함수와 클로저
sendable 함수와 클로저는 Sendable 프로토콜을 채택하는 대신에 @Sendable 속성으로 표시한다.
함수와 클로저가 캡쳐하는 모든 값은 Sendable이어야 한다. 또한, sendable 클로저는 값 타입 캡쳐만 사용해야 하며 캡쳐된 값은 sendable 타입이어야 한다.
sendable 클로저가 요구되는 context에서는, 요구사항을 만족하는 클로저가 암묵적으로 Sendable을 준수한다. 이에 대한 예시로 Task.detached(priority: operation)를 호출이 있다.
타입 어노테이션에 @Sendable을 작성하거나, 클로저의 매개변수 앞에 @Sendable을 작성함으로써 명시적으로 클로저를 sendable로 표시할 수 있다.
예를들어:
let sendableClosure = { @Sendable (number: Int) -> String in
if number > 12 {
return "More than a dozen."
} else {
return "Less than a dozen."
}
}Sendable Tuples
Sendable 프로토콜의 요구사항을 만족하기 위해서, 모든 튜플의 요소는 sendable을 만족해야 한다. 요구사항을 만족하는 튜플은 자동으로 Sendable을 만족하게 된다.
Sendable Metatypes
Int.Type과 같은 메타타입들은 Sendable 프로토콜을 준수한다.