🍏/Swift

[Swift] Sendable

eungding 2023. 4. 8. 15:14
728x90
반응형

Sendable 문서WWDC 22 > Eliminate data races using Swift Concurrency 를 조합해서 

재구성한 내용입니다. 


 

[1] Sendable  = safe to share in concurrency

 

Sendable 프로토콜은 concurrency 환경에서  안전하게 공유될 수 있는 타입을 나타냅니다. 

 

 

즉 우리는

하나의 concurrency domain (또는 isolation domain)에서 다른 domain으로

sendable type을 안전하게 pass 할 수 있습니다.  (data race를 만들지 않고)

 

 

 

[2] Sendable 을 채택할 수 있는 타입 

 

1. Value Types 

 

frozen struct or enumeration.

 

아래 사진에 나오는 Chicken 은 class인데 이를 프로퍼티로 들고 있으면 당연히 X 

 

 

2. Actor 

 

모든 actor 타입은 암묵적으로 Sendable 을 conform 하고 있습니다. 

actor는 mutable state에 대한 isolation을 보장하기 때문입니다. 

 

 

3. Class (final + no mutable storage + no superclass)

 

아래 요구사항을 충족시키는 class는  sendable을 채택할 수 있습니다.

 

- Be marked final
- Contain only stored properties that are immutable and sendable
- Have no superclass or have NSObject as the superclass

 

 

4. Class (marked with @MainActor)

 

@MainActor 로 마킹된 클래스는 암묵적으로 sendbale 입니다.

main actor가 상태에 대한 모든 액세스를 조정하기 때문입니다. 

이러한 클래스는 mutable and nonsendable 를 가질 수 있습니다. 

 

 

5. Functions and closures (by marking them with @Sendable)

 

원래 일반적으로 function type에 프로토콜을 채택할 수 없지만 Sendable은 특별합니다. 

@Sendable attribute 를 마킹하는 방식으로 function이나 closure가 sendable을 채택하도록 할 수 있습니다. 

 

type annotation의 한 부분으로 적어도 되고, 클로저 파라미터들 앞에 적어도 됩니다. 

 

 

function이나 closure가 캡쳐하는 어떤 value 든지 sendable 이여야합니다. 

 

 

6. Tuples

튜플의 모든 elements들이 Sendable 이라면 해당 튜플은 암묵적으로 Sendable을 채택합니다.

 

 

7. Meta Type

Int.Type 같은 메타타입은 암묵적으로  Sendable을 채택합니다.

 

 

 

[3] Sendable constraints in Task APIs 

 

Task APIs 에 Sendable이 주로 사용되고 있습니다. 

 

 

# 1.  Task의 Success 타입 

 

 

이런 정의로 인해

예를들어 Sendable을 채택안하는 Chicken 을 Success 타입으로 하면 

워닝이 나게 됩니다. 

 

 

참고로  Xcode Build Setting > Strict Concurrency Checking 을 보면 Minimal이 디폴트로 되어있어요

Targeted 또는 Complete 로 바꾸면 워닝을 보실 수 있습니다.

 

 

Targeted 는 다른 모듈의 워닝은 안나오게 하는 옵션인데,  @preconcurrency import 를 해야한다고 합니다.

 

 

 

이제 WWDC랑 비슷하게 코드를 돌려보면 워닝이 잘나옵니다. 

 

 

 

 

# 2.  Task.detached > function type

 

detached의 function type에도 sendable이 쓰입니다.

 

 

근데 문서에는 @Sendable이 없고 API에는 있는데, 문서에 누락된 걸까요..? 

 

 

아무튼 이렇게 Task API에 sendable이 주로 사용되고

Sendable checking이 task isolation을 보장해준다고 합니다! 

 

 

 

 

[4]  @unchecked Sendable

 

@unchecked Sendable 을 쓰면 컴타일 타임의 체크를 disable 시키고

"내가 sendable인 지 직접 체크하겠다."  하는 것입니다. 

 

예를들어 3.1 에서 이렇게 워닝이 났었는데,

 

 

@uncheked Sendable을 채택하면 워닝이 사라지는 것을 볼 수 있습니다.

 

 

@uncheked Sendable 은

sendable을 내가 직접 체크하고 보장하겠다 ~~  할 때, 사용합니다.  

 

 

 

또한 이미지 쪽 문서보다가  @unchecked Sendable  을 많이 본 기억이 있습니다. 

예를들어 Orientation , ResizingMode .. 

 

https://developer.apple.com/documentation/uikit/uiimage/orientation

 

 

 enum 앞에 @frozen이 안붙어서 그런 것 같네요. 

 

 

 

+ GPT 의 깔끔 설명 

 

Swift의 동시성 모델에서는 Sendable 프로토콜을 사용해 여러 스레드 간에 안전하게 전달될 수 있는 타입을 정의합니다.
기본적으로 Swift 컴파일러는 타입이 Sendable을 준수하는지 자동으로 검사합니다. 
Sendable을 준수하기 위해서는 타입이 동시성 상황에서 데이터 레이스와 같은 문제를 발생시키지 않도록 스레드 안전하게 설계되어야 합니다.

그러나 일부 경우에는 타입이 실제로는 스레드 안전하지만, 컴파일러가 그 안전성을 자동으로 확인할 수 없는 경우가 있습니다.
예를 들어, 특정 내부 구현이 스레드 안전한 경우입니다. 이럴 때는 개발자가 해당 타입이 Sendable임을 컴파일러에게 명시할 수 있지만, @unchecked를 붙여 컴파일러가 자동 검사를 생략하도록 지시할 수 있습니다.

final class MyClass: @unchecked Sendable {
      let name: String // 비동기 환경에서 안전하게 사용된다는 것을 보장하는 타입
}

이 예시에서 MyClass는 Sendable을 준수한다고 명시되었지만, 컴파일러는 이를 직접 검증하지 않습니다. 대신, 개발자가 MyClass의 인스턴스가 여러 스레드에서 안전하게 사용될 수 있음을 보장해야 합니다.

@unchecked Sendable은 매우 신중하게 사용해야 하는 기능입니다. 

컴파일러가 스레드 안전성을 확인하지 않으므로, 동시성 문제를 스스로 철저히 검토해야 합니다. 

아래의 경우에 사용될 수 있습니다:
ㄴ 타입의 내부가 실제로 스레드 안전하게 설계되었지만, 컴파일러가 이를 자동으로 인식하지 못하는 경우.
ㄴ 타입이 주로 불변이거나, 동시성을 고려한 구조로 되어 있을 때.

 

 

 

 

 

반응형