Combine
PassthroughSubject에 대해서 설명하시오
PassthroughSubject
는 Combine 프레임워크에서 제공하는 클래스로, 외부에서 값을 직접 발행할 수 있도록 하는 Publisher입니다. 이 Subject는 아무 값이나 발행하지 않고, 외부로부터 명시적으로 발행된 값을 전달합니다. 주로 프로그래머가 직접 이벤트를 발행해야 하는 경우에 사용됩니다.
예를 들어, 버튼 클릭 이벤트를 스트림으로 처리하거나, 사용자가 직접 발행해야 하는 커스텀 이벤트에 사용될 수 있습니다.
import Combine
let subject = PassthroughSubject<String, Never>()
subject.sink { value in
print("Received value: \(value)")
}
subject.send("Hello, World!")
@Published에 대해서 설명하시오
@Published
는 SwiftUI와 Combine 프레임워크에서 사용되는 속성 래퍼(Property Wrapper)로, 값이 변경될 때마다 자동으로 이벤트를 발행하는 Publisher를 생성합니다. 주로 SwiftUI의 상태 관리를 위해 사용되며, 뷰가 모델의 상태 변화를 감지하고 이에 따라 업데이트될 수 있도록 합니다.
import Combine
import SwiftUI
class ViewModel: ObservableObject {
@Published var text: String = "Hello"
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
Text(viewModel.text)
}
}
AnyCancellable에 대해서 설명하시오
AnyCancellable
은 Combine 프레임워크에서 제공하는 타입으로, Publisher의 구독을 관리하고, 구독을 취소할 수 있는 핸들을 제공합니다. 주로 구독을 수동으로 취소하거나, 구독이 필요 없을 때 자동으로 취소되도록 할 때 사용됩니다. AnyCancellable
은 구독을 취소하기 위해 호출할 cancel()
메서드를 가지고 있습니다.
import Combine
var cancellable: AnyCancellable?
let subject = PassthroughSubject<String, Never>()
cancellable = subject.sink { value in
print("Received value: \(value)")
}
subject.send("Hello")
// 구독 취소
cancellable?.cancel()
sink에 대해서 설명하시오
sink
는 Combine 프레임워크에서 Publisher의 이벤트를 구독하고, 이벤트를 처리하는 클로저를 제공하는 메서드입니다. sink
는 Publisher가 발행하는 값을 받아 처리하는 방법을 정의할 수 있도록 합니다. 이를 통해 Publisher의 값이 변경될 때마다 특정 동작을 수행할 수 있습니다.
import Combine
let subject = PassthroughSubject<String, Never>()
let cancellable = subject.sink { value in
print("Received value: \(value)")
}
subject.send("Hello, Combine!")
throttle과 debounce의 차이점을 설명하시오
throttle
과 debounce
는 이벤트 스트림을 제어하는 데 사용되는 연산자로, 이벤트 발생 빈도를 조절하는 데 사용됩니다.
throttle
: 주어진 시간 간격 동안 발생한 첫 번째 이벤트만 발행합니다. 이 연산자는 주기적으로 이벤트를 방출하여 이벤트 발생 빈도를 제어합니다. 지정된 간격 동안 다른 이벤트가 무시됩니다.
import Combine
let subject = PassthroughSubject<String, Never>()
let throttled = subject.throttle(for: .seconds(1), scheduler: RunLoop.main, latest: true)
let cancellable = throttled.sink { value in
print("Received value: \(value)")
}
debounce
: 주어진 시간 간격 동안 발생한 마지막 이벤트를 발행합니다. 이 연산자는 지정된 간격 동안 이벤트가 더 이상 발생하지 않을 때까지 대기한 후 마지막 이벤트를 방출합니다.
import Combine
let subject = PassthroughSubject<String, Never>()
let debounced = subject.debounce(for: .seconds(1), scheduler: RunLoop.main)
let cancellable = debounced.sink { value in
print("Received value: \(value)")
}
Data를 Binding 하는 방법에 대해서 설명하시오
SwiftUI에서 Binding
은 두 개의 뷰 간에 데이터의 상태를 동기화하는 데 사용됩니다. @State
, @ObservedObject
, @EnvironmentObject
등을 사용하여 상태를 관리할 때, Binding
을 사용하면 부모 뷰의 상태를 자식 뷰에 전달하여 양방향 데이터 바인딩을 구현할 수 있습니다.
import SwiftUI
struct ContentView: View {
@State private var text: String = "Hello"
var body: some View {
VStack {
TextField("Enter text", text: $text)
Text("You entered: \(text)")
}
}
}
여기서 TextField
는 $text
바인딩을 사용하여 text
상태 변수를 업데이트합니다.
Combine에서 Just와 Future의 차이점은 무엇인가요?
Just:
Just
는 단일 값을 즉시 발행하는 Publisher입니다. 성공적으로 한 번의 값만 발행하고 완료(completion)됩니다. 주로 테스트나 간단한 값 발행에 사용됩니다.import Combine let justPublisher = Just("Hello, Combine!") justPublisher.sink(receiveCompletion: { completion in print("Completed: \(completion)") }, receiveValue: { value in print("Received value: \(value)") })
Future:
Future
는 비동기 작업의 결과를 나중에 발행하는 Publisher입니다. 비동기 작업의 성공 또는 실패 결과를 한 번 발행합니다. 주로 네트워크 요청이나 다른 비동기 작업에 사용됩니다.import Combine let futurePublisher = Future<String, Error> { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success("Hello, Future!")) } } futurePublisher.sink(receiveCompletion: { completion in print("Completed: \(completion)") }, receiveValue: { value in print("Received value: \(value)") })
Combine의 flatMap 연산자는 언제 사용하나요?
flatMap
연산자는 여러 Publisher를 하나의 스트림으로 합치는 데 사용됩니다. 각 입력 값에 대해 새로운 Publisher를 생성하고, 이 Publisher들이 발행하는 모든 값들을 단일 스트림으로 병합합니다. 주로 비동기 작업이 중첩되는 상황에서 사용됩니다.
import Combine
let numbers = [1, 2, 3].publisher
let flatMapped = numbers.flatMap { number in
return Just(number * 2)
}
flatMapped.sink(receiveCompletion: { completion in
print("Completed: \(completion)")
}, receiveValue: { value in
print("Received value: \(value)")
})
Combine에서 merge와 combineLatest의 차이점은 무엇인가요?
merge:
merge
연산자는 여러 Publisher를 병합하여 하나의 스트림으로 만듭니다. 각 Publisher가 발행하는 이벤트를 시간 순서대로 방출합니다.import Combine let publisher1 = PassthroughSubject<String, Never>() let publisher2 = PassthroughSubject<String, Never>() let merged = publisher1.merge(with: publisher2) merged.sink { value in print("Received value: \(value)") } publisher1.send("Hello") publisher2.send("World")
combineLatest:
combineLatest
연산자는 여러 Publisher의 최신 값을 결합하여 하나의 스트림으로 만듭니다. 모든 Publisher가 적어도 한 번 값을 발행해야 결합된 값을 방출합니다.import Combine let publisher1 = PassthroughSubject<String, Never>() let publisher2 = PassthroughSubject<String, Never>() let combined = publisher1.combineLatest(publisher2) combined.sink { value1, value2 in print("Received values: \(value1), \(value2)") } publisher1.send("Hello") publisher2.send("World")
@ObservedObject와 @StateObject의 차이점은 무엇인가요?
@ObservedObject:
@ObservedObject
는 다른 곳에서 소유하는 객체를 관찰하기 위해 사용됩니다. 이 객체의 상태가 변경되면 해당 뷰가 업데이트됩니다. 객체의 생명주기를 관리하지 않기 때문에 부모 뷰가 객체를 소유합니다.class ViewModel: ObservableObject { @Published var text: String = "Hello" } struct ContentView: View { @ObservedObject var viewModel = ViewModel() var body: some View { Text(viewModel.text) } }
@StateObject:
@StateObject
는 뷰 내부에서 객체를 소유하고 관리하기 위해 사용됩니다. 이 객체의 생명주기를 뷰가 책임지며, 뷰가 초기화될 때 한 번만 초기화됩니다.class ViewModel: ObservableObject { @Published var text: String = "Hello" } struct ContentView: View { @StateObject var viewModel = ViewModel() var body: some View { Text(viewModel.text) } }
Combine의 catch 연산자는 어떻게 사용되나요?
catch
연산자는 에러가 발생했을 때 대체 Publisher를 제공하여 스트림을 복구하는 데 사용됩니다. 에러가 발생하면 대체 Publisher가 값을 발행하여 스트림이 끊기지 않도록 합니다.
import Combine
let subject = PassthroughSubject<Int, Error>()
let caught = subject.catch { error in
return Just(0)
}
caught.sink(receiveCompletion: { completion in
print("Completed: \(completion)")
}, receiveValue: { value in
print("Received value: \(value)")
})
subject.send(completion: .failure(NSError(domain: "", code: -1, userInfo: nil)))
SwiftUI에서 @EnvironmentObject는 무엇이고, 언제 사용하나요?
@EnvironmentObject
는 뷰 계층 구조 전체에 걸쳐 공유되는 객체를 관리하기 위해 사용됩니다. 주로 앱의 전역 상태를 관리하거나 여러 뷰 간에 공유해야 하는 데이터를 전달할 때 사용됩니다.
class AppState: ObservableObject {
@Published var count: Int = 0
}
struct ContentView: View {
@EnvironmentObject var appState: AppState
var body: some View {
VStack {
Text("Count: \(appState.count)")
Button("Increment") {
appState.count += 1
}
}
}
}
Combine에서 Publisher와 Subscriber의 관계를 설명하세요.
Publisher:
Publisher
는 데이터를 생성하고 발행하는 객체입니다.Publisher
는 여러 이벤트(값 또는 완료/실패 이벤트)를 발행할 수 있습니다.Publisher
는subscribe
메서드를 통해Subscriber
와 연결됩니다.Subscriber:
Subscriber
는Publisher
가 발행하는 데이터를 수신하는 객체입니다.Subscriber
는receive(subscription:)
,receive(_:)
,receive(completion:)
메서드를 통해 데이터를 처리합니다.
두 객체는 subscribe
메서드를 통해 연결되며, Publisher
가 데이터를 발행하면 Subscriber
가 이를 수신하여 처리합니다.
import Combine
let publisher = Just("Hello, Combine!")
let subscriber = Subscribers.Sink<String, Never> { completion in
print("Completed: \(completion)")
} receiveValue: { value in
print("Received value: \(value)")
}
publisher.subscribe(subscriber)
Combine의 switchToLatest 연산자는 어떻게 작동하나요?
switchToLatest
연산자는 여러 Publisher가 발행하는 값 중 가장 최근에 발행된 Publisher의 값을 구독하여 방출합니다. 주로 최신 데이터만 필요할 때 사용됩니다.
import Combine
let subject1 = PassthroughSubject<String, Never>()
let subject2 = PassthroughSubject<String, Never>()
let subjects = PassthroughSubject<PassthroughSubject<String, Never>, Never>()
let switched = subjects.switchToLatest()
let cancellable = switched.sink { value in
print("Received value: \(value)")
}
subjects.send(subject1)
subject1.send("Hello from subject1")
subjects.send(subject2)
subject2.send("Hello from subject2")
subject1.send("Another value from subject1") // 이 값은 무시됩니다.
Combine의 assign 연산자는 어떤 역할을 하나요?
assign
연산자는 Publisher가 발행하는 값을 특정 객체의 속성에 할당하는 데 사용됩니다. 주로 뷰 모델의 속성을 업데이트하거나 상태를 관리할 때 사용됩니다.
import Combine
class ViewModel: ObservableObject {
@Published var text: String = ""
}
let viewModel = ViewModel()
let subject = PassthroughSubject<String, Never>()
let cancellable = subject.assign(to: \.text, on: viewModel)
subject.send("Hello, Combine!")
print(viewModel.text) // 출력: "Hello, Combine!"
Combine에서 메모리 관리를 어떻게 하나요? (예: AnyCancellable과 store 메서드)
Combine에서 메모리 관리는 주로 AnyCancellable
과 store
메서드를 사용하여 구독을 관리합니다. 구독을 취소하지 않으면 메모리 누수가 발생할 수 있으므로, 적절한 시점에 구독을 취소하는 것이 중요합니다.
AnyCancellable:
AnyCancellable
은 구독을 취소할 수 있는 객체로, 구독을 명시적으로 취소하거나, 객체가 메모리에서 해제될 때 자동으로 취소됩니다.var cancellable: AnyCancellable? let subject = PassthroughSubject<String, Never>() cancellable = subject.sink { value in print("Received value: \(value)") } subject.send("Hello") cancellable?.cancel()
store:
store
메서드는 여러AnyCancellable
을 관리하기 위해Set<AnyCancellable>
에 구독을 저장합니다. 뷰 또는 뷰 모델의 생명주기와 함께 구독을 관리할 때 유용합니다.import Combine class ViewModel: ObservableObject { var cancellables = Set<AnyCancellable>() init() { let subject = PassthroughSubject<String, Never>() subject.sink { value in print("Received value: \(value)") } .store(in: &cancellables) } } let viewModel = ViewModel()
이렇게 하면 ViewModel
이 해제될 때 모든 구독이 자동으로 취소됩니다
Last updated