🔥 Sendable 타입

446자
6분

태스크와 액터를 사용하면 프로그램을 안전하게 동시에 실행할 수 있는 조각으로 나눌 수 있습니다. 태스크나 액터 인스턴스 내부에서 변수나 속성과 같은 변경 가능한 상태를 포함하는 프로그램 부분을 동시성 도메인이라고 합니다. 동시성 도메인 간에는 일부 종류의 데이터를 공유할 수 없는데, 그 이유는 해당 데이터가 변경 가능한 상태를 포함하지만 중첩된 접근으로부터 보호하지 않기 때문이죠.

한 동시성 도메인에서 다른 동시성 도메인으로 공유할 수 있는 타입을 Sendable 타입이라고 합니다. 예를 들어, 액터 메서드를 호출할 때 인수로 전달하거나 태스크의 결과로 반환할 수 있습니다. 이 장의 앞부분에 나온 예제에서는 Sendable에 대해 논의하지 않았는데, 그 이유는 해당 예제에서 동시성 도메인 간에 전달되는 데이터에 대해 항상 안전한 단순한 값 타입을 사용했기 때문입니다.

반면에 일부 타입은 동시성 도메인 간에 전달하기에 안전하지 않습니다. 예를 들어, 변경 가능한 속성을 포함하고 해당 속성에 대한 접근을 직렬화하지 않는 클래스는 서로 다른 태스크 간에 해당 클래스의 인스턴스를 전달할 때 예측할 수 없고 잘못된 결과를 생성할 수 있죠.

Sendable 프로토콜을 따르도록 선언하여 타입을 Sendable로 표시합니다. 해당 프로토콜에는 코드 요구 사항이 없지만 Swift가 적용하는 의미론적 요구 사항이 있습니다. 일반적으로 타입이 Sendable이 되는 방법에는 세 가지가 있습니다:

  • 타입이 값 타입이고, 변경 가능한 상태가 다른 Sendable 데이터로 구성되는 경우입니다. 예를 들어, Sendable 저장 속성을 가진 구조체나 Sendable 연관 값을 가진 열거형이 있죠.
  • 타입에 변경 가능한 상태가 없고, 불변 상태가 다른 Sendable 데이터로 구성되는 경우입니다. 예를 들어, 읽기 전용 속성만 있는 구조체나 클래스가 있습니다.
  • 타입에 변경 가능한 상태의 안전성을 보장하는 코드가 있는 경우입니다. 예를 들어, @MainActor로 표시된 클래스나 특정 스레드 또는 큐에서 속성에 대한 접근을 직렬화하는 클래스가 있죠.

의미론적 요구 사항의 자세한 목록은 Sendable 프로토콜 참조를 확인하세요.

일부 타입은 항상 Sendable입니다. 예를 들어, Sendable 속성만 있는 구조체나 Sendable 연관 값만 있는 열거형이 있죠. 예를 들면 다음과 같습니다:

struct TemperatureReading: Sendable {
    var measurement: Int
}
 
extension TemperatureLogger {
    func addReading(from reading: TemperatureReading) {
        measurements.append(reading.measurement)
    }
}
 
let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)
swift

TemperatureReading은 Sendable 속성만 있는 구조체이고, 구조체에 public 또는 @usableFromInline 표시가 없기 때문에 암시적으로 Sendable입니다. 다음은 Sendable 프로토콜에 대한 일치가 암시되는 구조체 버전입니다:

struct TemperatureReading {
    var measurement: Int
}
swift

Sendable 프로토콜에 대한 암시적 일치를 재정의하여 타입이 Sendable이 아님을 명시적으로 표시하려면 extension을 사용하면 됩니다:

struct FileDescriptor {
    let rawValue: CInt
}
 
@available(*, unavailable)
extension FileDescriptor: Sendable { }
swift

위의 코드는 POSIX 파일 디스크립터에 대한 래퍼의 일부를 보여줍니다. 파일 디스크립터에 대한 인터페이스가 열린 파일을 식별하고 상호 작용하기 위해 정수를 사용하고, 정수 값은 Sendable이지만, 파일 디스크립터는 동시성 도메인 간에 전송하기에 안전하지 않습니다.

위의 코드에서 FileDescriptor는 암시적으로 Sendable이 되기 위한 기준을 충족하는 구조체입니다. 그러나 extension은 Sendable에 대한 일치를 사용할 수 없게 만들어 해당 타입이 Sendable이 되는 것을 방지합니다.

Swift에서 Sendable 타입을 사용하면 동시성 도메인 간에 데이터를 안전하게 전달할 수 있어 병렬 프로그래밍의 안정성과 효율성을 높일 수 있답니다.