Migrate to Swift 6(2) - Sendable protocol

이정훈·5일 전
0

Swift 파헤치기

목록 보기
12/12
post-thumbnail

Swift Language Mode가 버전 업이 되면서 동시성 환경에서 데이터 경합(Data Race)이 발생할 여지가 있는 경우 Swift 5에서는 경고로 끝났지만 Swift 6에서는 Compile 단계에서 이것을 사전에 방지하도록 강제하고 있다. 이번 포스트에서는 동시성 환경에서 데이터 경합을 방지할 수 있는 두번째 개념 Sendable protocol에 대해 알아본다.

Sendable protocol이란?

공식 문서에 등장하는 Sendable protocol의 개념은 다음과 같다.

Sendable

임의의 동시성 context에서 데이터 경합 없이 thread-safe하게 값을 전달할 수 있는 타입

하나의 동시성 도메인에서 다른 동시성 도메인으로 값을 전달할 때 Sendable 타입을 전달할 수 있다. 예를 들면 actor의 메서드의 인자로 Sendable 타입을 전달할 수 있다.

일반적으로 Sendable protocol을 가장 흔히 볼 수 있는 곳이 Task의 제네릭 파라미터 Success에서 확인할 수 있다. Task는 새로운 비동기 작업을 생성하여 동시적으로 실행될 수 있으므로 Task로 전달되는 타입은 Sendable protocol을 채택하여 데이터 경합을 방지할 수 있어야한다.

struct Task<Success: Sendable, Failure: Error> {
    var value: Success {
        get async throws {}
    }
}

따라서 아래와 같이 Task에 전달되는 타입이 Chicken class와 같이 Sendable protocol을 채택하고 있지 않는 경우 컴파일 에러가 발생하게 된다.

Sendable protocol을 채택할 수 있는 타입

  • Value types

  • Reference types with no mutable storage

  • Reference types that internally manage access to their state

  • Functions and closures (by marking them with @Sendable)

Sendable protocol은 자체적인 프로퍼티나 메서드 구현 요구사항이 있진 않지만 컴파일 레벨에서 지켜야할 의미적 요구 사항이 존재하며 그 내용은 다음과 같다.

구조체 & 열거형

struct 타입과 enum 타입에서 Sendable protocol을 채택하기 위해서는 구조체열거형의 멤버와 associated value들은 모두 Sendable protocol을 채택해야 한다.

또한 아래의 요구사항을 충족하는 경우 암시적으로 Sendable을 채택한다.

  • Frozen 구조체열거형
  • public으로 선언 되지 않거나 @usableFromInline로 표시되지 않은 경우

actor

이전 포스트에서 다루었던 actor 타입 또한 암묵적으로 Sendable protocol을 채택한다.

class

class 타입에서 Sendable protocol을 채택하기 위해서는 다음과 같은 요구사항을 필요로 한다.

  • final 클래스
  • 저장 프로퍼티immutable 하거나 Sendable protocol을 채택한 경우
  • 부모 클래스가 존재하지 않거나 NSObject를 부모 클래스로 가지는 경우

또한 클래스@MainActor로 선언되어 있는 경우 암묵적으로 Sendable protocol을 따른다.

@MainActor로 선언되어 있는 경우 메인 스레드에서 동작을 보장하기 때문에 데이터 경합이 일어나지 않는다.

함수 & 클로저

함수는 기본적으로 protocol을 채택하는 것이 불가능하지만 예외적으로 @Sendable 속성을 사용하여 함수와 클로저가 Sendable을 채택하게 할 수 있다. 클로저의 경우 클로저가 캡쳐하는 값은 모두 Sendable을 채택해야 한다. 또한 Sendable 클로저가 값을 캡쳐할 때, 값 복사에 의해 캡쳐한다.

let sendableClosure = { @Sendable (number: Int) -> String in
    if number > 12 {
        return "More than a dozen."
    } else {
        return "Less than a dozen"
    }
}

Tuple

튜플 내부 요소가 모두 Sendable protocol을 채택한다면, 암묵적으로 해당 튜플Sendable protocol을 채택한다.

Meta Type

Int.Type과 같이 메타 타입의 경우 암묵적으로 Sendable protocol을 채택한다.

Reference

https://developer.apple.com/documentation/swift/sendable

profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글