What is the Sendable protocol in Swift? | Swift Concurrency #11
async
)에서 특정 데이터를 보내도 안전한지 점검하는 프로토콜Sendable
프로토콜을 통해 스레드 안전 보장 가능Sendable
프로토콜을 내부에서 따르고 있음, Sendable
프로토콜을 명시한다면 약소한 퍼포먼스 증가 기대 가능(컴파일러)final
클래스 및 클래스 내 변수가 아닌 상수 타입으로 선언함으로써 Sendable
프로토콜 사용 가능@unchekced
: 해당 키워드를 통해 컴파일러에게 Sendable
프로토콜을 따르는지 확인하지 않도록 강제하기 때문에 위험. 스레드 안전성을 위해 해당 클래스 내부에서 커스텀 큐를 통해 동기화 기법을 제공해야 함(Sendable
프로토콜을 따르는 해당 클래스 내의 값을 변수로 선언, 업데이트가 가능하도록 만들기 위함)struct SendableBootCampUserInfoStruct: Sendable {
// Value Type Struct -> Thread Safe
let name: String
var name2: String = ""
/*
var -> can be sendable
*/
}
/*
if class -> Sendable, then ...
Non-final class 'SendableBootCampUserInfoClass' cannot conform to 'Sendable'; use '@unchecked Sendable'
*/
final class SendableBootCampUserInfoClass: Sendable {
let name: String
/*
if name -> var, then ...
Stored property 'name' of 'Sendable'-conforming class 'SendableBootCampUserInfoClass' is mutable
*/
init(name: String) {
self.name = name
}
}
Sendable
프로토콜을 기본적으로 따르고 있고, 내부에 변수/상수로 선언하든 관계없이 스레드 안전을 보장하는 값 타입struct SendableBootCampUserInfoStruct: Sendable {
// Value Type Struct -> Thread Safe
let name: String
var name2: String = ""
/*
var -> can be sendable
*/
}
struct SendableBootCampUserInfoStruct2 {
let name: String
var name2: String = ""
// Whether or not using Sendable keyword, it would be sendable
// but using Sendable protocol keyword, its performance would be better
}
/*
if class -> Sendable, then ...
Non-final class 'SendableBootCampUserInfoClass' cannot conform to 'Sendable'; use '@unchecked Sendable'
*/
final class SendableBootCampUserInfoClass: Sendable {
let name: String
/*
if name -> var, then ...
Stored property 'name' of 'Sendable'-conforming class 'SendableBootCampUserInfoClass' is mutable
*/
init(name: String) {
self.name = name
}
}
/*
even though you do not use keyword final class or let, they would be sendable confirmed using @unchecked but also very dangerous
*/
class SendalbeBootCampUserInfoClass2: @unchecked Sendable {
var name: String
init(name: String) {
self.name = name
}
}
final class SendableBootCampUserInfoClass3: @unchecked Sendable {
private var name: String
let queue = DispatchQueue(label: "CustomQueue")
init(name: String) {
self.name = name
}
func updateName(name: String) {
queue.async {
self.name = name
}
}
// ensure thread-safety using custom queue(lock)
}
@unchecked
를 통해 컴파일러에게 Sendable
프로토콜을 체크하지 않기를 강제한 해당 클래스 내부에서는 커스텀 큐를 만들어서 직접 스레드 안전을 보장해야 한다는 데 주의import SwiftUI
actor SendableBootCampActor {
func updateDataFromDatabase(userInfo: SendableBootCampUserInfoStruct) {
// Update Data
}
func updateDataFromDatabase2(userInfo: SendableBootCampUserInfoClass) {
// Update Data
}
}
class SendableBootCampViewModel: ObservableObject {
let dataService = SendableBootCampActor()
func updateDataFromDatabase() async {
let userInfo = SendableBootCampUserInfoStruct(name: "User Info")
await dataService.updateDataFromDatabase(userInfo: userInfo)
}
func updateDataFromDatabase2() async {
let userInfo = SendableBootCampUserInfoClass(name: "User Info")
await dataService.updateDataFromDatabase2(userInfo: userInfo)
}
}
Sendable
프로토콜struct SendableBootCamp: View {
@StateObject private var viewModel = SendableBootCampViewModel()
var body: some View {
Text("Sendable Protocol Usage")
.task {
await viewModel.updateDataFromDatabase()
}
}
}