2024년 첫 포스팅을 Sendable
로 하게 되었습니다~
(작년에 하려고 한 건데 이게 맞나)
이 글을 보시는 분들 2024년 한 해도 잘 보내시길 바랍니다 0.<~
해당 코드는 swift > stdlib > public > core > Sendable.swift에서 볼 수 있습니다.
@_marker public protocol Sendable { }
복사를 통해 동시성 도메인 간에 값을 안전하게 전달할 수 있는 타입입니다.
한 동시성 도메인에서 다른 동시성 도메인으로Sendable
한 타입의 값을 안전하게 전달할 수 있습니다. 예를 들어 행위자의 메소드를 호출할 때Sendable
한 값을 인수로 전달할 수 있습니다. 다음은 모두Sendable
으로 표시될 수 있습니다.
- 값 타입
- 변경 가능한 저장소가 없는 참조 타입
- 내부적으로 상태에 대한 액세스를 관리하는 참조 타입
- 함수 및 클로저(
@Sendable
로 표시)이 프로토콜에는 필수 메서드나 프로퍼티가 없지만 컴파일 타임에 적용되는 의미론적 요구 사항이 있습니다. 이러한 요구 사항은 아래 섹션에 나열되어 있습니다.
Sendable
에 대한 적합성은 해당 타입의 선언과 동일한 파일에서 선언되어야 합니다.컴파일러 적용 없이
Sendable
에 대한 적합성을 선언하려면@unchecked Sendable
을 작성하세요. 예를 들어 잠금 또는 대기열을 사용하여 해당 상태에 대한 모든 액세스를 보호함으로써 확인되지 않은Sendable
타입의 정확성에 대한 책임이 있습니다.Sendable
에 대한 확인되지 않은 적합성은 적합성이 동일한 파일에 있어야 한다는 규칙의 적용을 비활성화합니다.
즉, Sendable
은 동시성 과정에서 안전하게 값을 전달하는 타입입니다. 따로 해당 프로토콜에 필수 메서드나 프로퍼티가 없지만 의미상 Sendable
은 해당 타입의 선언과 동일한 파일에서 선언되어야 합니다. 어렵게 보이지만 별로 어려운 이야기는 아닙니다. Sendable
을 적용할 타입과 같은 파일 안에서 채택이 되어야 한다는 통상적인 의미로 보입니다.
컴파일러 적용 없이 @unchecked Sendable
을 선언하여 직접 Sendable
한지 체크하는 방법도 있습니다. 어떤 값이 오는지 확실하게 알 때만 사용하는 것이 좋아보입니다.
Sendable
프로토콜의 요구 사항을 충족하려면 열거형 또는 구조체에 전송 가능한 멤버 및 관련 값만 있어야 합니다. 어떤 경우에는 요구 사항을 충족하는 구조체와 열거형이 암시적으로Sendable
을 준수합니다.
- 고정된 구조 및 열거형
- 구조체 및 열거형
공개되지 않고@usableFromInline
으로 표시되지 않습니다.그렇지 않으면
Sendable
에 대한 적합성을 명시적으로 선언해야 합니다.보낼 수 없는 저장된 프로퍼티와 보낼 수 없는 관련 값이 있는 열거형이 있는 구조체는
@unchecked Sendable
로 표시되어Sendable
프로토콜의 의미론적 요구 사항을 충족하는지 수동으로 확인한 후 컴파일 시간 정확성 검사를 비활성화할 수 있습니다.
Sendable
프로토콜의 요구 사항을 충족하려면 열거형 또는 구조에 전송 가능한 멤버 및 관련 값만 있어야 한다는 것을 보면, 참조가 있을 경우에는 Sendable
을 채택할 수 없는 거 같습니다. 이러한 요구사항을 충족하면 암시적으로 Sendable
을 준수한다고 합니다.
그래서 코린이 시절.. 제가 동시성 프로그래밍할 때 작업할 값타입에 Sendable
을 준수하지 않아도 잘 동작했던 것이었군요...!
Actor
는 변경 가능한 상태에 대한 모든 액세스가 순차적으로 수행되도록 보장하므로 모든Actor
타입은 암시적으로Sendable
을 준수합니다.
오 Actor
에 대해서는 잘 몰라서 그러려니 하고 넘어가는데... 다음 포스팅은 Actor
에 대해서 한 번 다뤄봐야겠어요!
Sendable 프로토콜의 요구 사항을 충족하려면 클래스는 다음을 수행해야 합니다.
final
으로 표시됨- 변경할 수 없고 전송할 수 있는 저장된 속성만 포함
- 슈퍼클래스가 없거나
NSObject
를 슈퍼클래스로 사용
@MainActor
로 표시된 클래스는 기본 행위자가 해당 상태에 대한 모든 액세스를 조정하기 때문에 암시적으로 전송 가능합니다. 이러한 클래스에는 변경 가능하고 전송할 수 없는 속성이 저장되어 있을 수 있습니다.위의 요구 사항을 충족하지 않는 클래스는
@unchecked Sendable
로 표시되어 Sendable 프로토콜의 의미론적 요구 사항을 충족하는지 수동으로 확인한 후 컴파일 시간 정확성 검사를 비활성화할 수 있습니다.
참조타입인 클래스는 수정에 대한 가능성을 컴파일러가 알 수 없기 때문에 저러한 제약이 생긴 거 같습니다.
Sendable
프로토콜을 따르는 대신@Sendable
프로퍼티를 사용하여 전송 가능한 함수와 클로저를 표시합니다. 함수나 클로저가 캡처하는 모든 값은 전송 가능해야 합니다. 또한 전송 가능한 클로저는 값별 캡처만 사용해야 하며 캡처된 값은 전송 가능한 타입이어야 합니다.전송 가능한 클로저를 기대하는 컨텍스트에서 요구 사항을 충족하는 클로저는 묵시적으로
Sendable
을 준수합니다(예:Task.detached(priority:Operation:)
호출에서).
@Sendable
을 타입 주석의 일부로 작성하거나 클로저 매개변수 앞에@Sendable
을 작성하여 클로저를 전송 가능으로 명시적으로 표시할 수 있습니다. 예를 들면 다음과 같습니다.
let sendableClosure = { @Sendable (number: Int) -> String in
if number > 12 {
return "More than a dozen."
} else {
return "Less than a dozen"
}
}
오... 클로저나 메서드도 Sendable을 적용할 수 있군요! 어트리뷰트로 적용을 할 수 있는 것처럼 보입니다! 연산을 통해서 해당 값을 동시성 프로그래밍에 적용을 할 때 사용하면 요긴할 거 같습니다 ㅎㅎ
Sendable
프로토콜의 요구 사항을 충족하려면 튜플의 모든 요소가 전송 가능해야 합니다. 요구 사항을 충족하는 튜플은 암시적으로Sendable
을 따릅니다.
Int.Type
과 같은 메타타입은 암시적으로Sendable
프로토콜을 따릅니다.
튜플과 메타타입도 암시적으로 Sendable
프로토콜을 준수한다고 합니다!
Sendable
은 복사를 통해 동시성 프로그래밍에서 안전하게 값을 보내주는 타입이기 때문에, 값의 안정성을 보장해줄 수 있는 타입입니다.
따란~ 이미 암시적으로 준수하고 있는 부분들이 많아서 따로 오류가 뜨지 않기 때문에 많이 쓰지는 않은 문법이었는데, 명시적으로 이 타입은 안전하다!를 표현해주는 방식으로도 활용해보면 좋을 거 같습니다~