[SwiftUI] Sendable

Junyoung Park·2022년 8월 29일
0

SwiftUI

목록 보기
64/136
post-thumbnail

What is the Sendable protocol in Swift? | Swift Concurrency #11

Sendable

Sendable 프로토콜의 정의

  • 비동기 환경(async)에서 특정 데이터를 보내도 안전한지 점검하는 프로토콜
  • 스레드 안전을 보장하는 액터, 특정 액터를 사용하는 여러 개의 스레드 안전하지 않은 클래스들 → Sendable 프로토콜을 통해 스레드 안전 보장 가능

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()
            }
    }
}
profile
JUST DO IT

0개의 댓글