Swift Concurrency의 등장으로 새롭게 정의된 프로토콜인 Sendable은 Concurrency 환경에서 Data Race를 방지하기 위해 등장했다.
Sendable 프로토콜은 Concurrency 환경에서 데이터를 안전하게 공유할 수 있다라고 컴파일러에게 알려주는 역할을 한다.
Although this protocol doesn’t have any required methods or properties, it does have semantic requirements that are enforced at compile time.
Sendable을 채택할 수 있는 상황은 아래와 같다.
1. Actor
아직 깊게 공부해보진 않았지만, 마찬가지로 Concurrency에서 등장한 Actor는 Sendable을 채택하고 있다.
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public protocol AnyActor : AnyObject, Sendable {}
Actor는 각각의 독립적인 상태(shared mutable state)를 가지고 있으며, 해당 상태에 대한 접근을 동기화(Others tasks must wait their turn)하여 Data Race를 방지한다.
즉, Concurrency 환경에서 데이터를 안전하게 공유할 수 있으므로 Actor는 Sendable 프로토콜을 채택할 수 있다.
2. Value Type (Eumeration, Struct)
값 타입의 경우 생성된 인스턴스의 값을 꺼내 변경할 시 각각의 독립적인 인스턴스가 생성되므로 하나의 인스턴스를 참조하여 값을 서로 변경하려하는 상황이 나오지 않기 떄문에 암시적으로 Sendable을 채택하고 있다.
다만 아래에 나오겠지만, 값 타입 인스턴스의 프로퍼티로 참조 타입(Sendable 프로토콜을 만족하지 않은)을 가지고 있을 경우엔 불가능하다.
struct Smoker: Sendable {
let qwe1: String
let qwe2: Smoker1 // Stored property 'qwe2' of 'Sendable'-conforming struct 'Smoker' has non-sendable type 'Smoker1'
}
class Smoker1 {
let qwe1: String = ""
var qwe2: Int = .zero
}
3. Class
클래스는 하나의 인스턴스를 참조하여 해당 인스턴스의 프로퍼티를 변경하려 들기때문에, 별도의 동기 처리를 하지않는다면 Thread Safe하지않다.
근데 이걸 막아준다면 클래스 또한 Sendable을 채택할 수 있다.
1. 모든 인스턴스 프로퍼티에 대한 접근을 동기 처리한다.
2. 모든 인스턴스 프로퍼티를 변수가 아닌 상수로 만들어 Thread Safe하게 만든다.
3. Final을 채택하여 상속을 막는다. (정확힌 모르겠지만, 하위 클래스가 상위 클래스를 변경할 수 있기때문에 인 것 같다고 예상을 해본다.)
Concurrency 이전에도 Swift는 비동기 환경에서의 상수의 경우 Thread Safe하여 Data Race를 방지할 수 있었지만, 변수의 경우엔 그렇지 않았다.
위의 식이 동일하게 적용됐다고 생각하면 편하다. 비동기를 가능케하는 Concurrency 환경에서 클래스 인스턴스 프로퍼티가 상수가 아닌 변수일 시엔 Thread lock을 거는 등의 방법을 사용하지않는 이상 Data Race를 방지할 수 없으므로 컴파일 에러를 띄운다.
class Smoker1: Sendable {
let qwe1: String = ""
var qwe2: Int = .zero // Stored property 'qwe2' of 'Sendable'-conforming class 'Smoker1' is mutable
}
댓글