- sink
- handleEvents(receiveOutput: )
- receive(on: ), subscribe(on: )
1. sink
- 애플 개발자 문서를 보면 sink 란 클로저 기반의 동작이 있는 subscriber를 절대 실패하지 않게 하는 publisher와 연결하는 것
- 그렇기 때문에 에러 타입은 Never 일때만 가능(그래서 회사 subject 에러 타입이 죄다 Never인 것)
func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
Return Value
- 수신 된 값의 할당을 종료할 때 사용하는 Cancellable 인스턴스를 리턴한다. 결과를 할당해제 한다면 sumbscription stream을 해제하게 된다.
parameter
- receiveValue: 값을 받을 때 실행하는 클로저
- receiveCompletion: stream이 성공적으로 종료되었음을 알려주는 클로저(구독이 종료됨을 알려주는 클로저)
2. handleEvents(receiveOutput:)
handleEvents()의 경우에는 publisher event가 발생할 때마다 수행된다.
그래서 UI 업데이트, 로킹 등의 부작용을 수행할 때마다 사용할 수 있는 operator라고 한다.
publisher의 라이프사이클에 있는 모든 이벤트를 가로채고, 각 단계에서 조치를 취할 수 있다.
뭐가 문젠지 모르겠을때 handleEvents를 사용해서 무슨 일이 일어나고 있는지 추적할 수 있다.
3. receive(on: ) & subscribe(on: )
publisher
subscriber
subject
scheduler
cancellable
receive에 대해 알아보기 전에 scheduler에 대해 먼저 살펴보겠다.
Scheduler
scheduler는 프로토콜이다.
scheduler는 closure의 실행시기와 방법을 정의하는 프로토콜이다.
이 scheduler를 이용해서 가능한 빨리 코드를 실행시킬 수도 있고, 특정 시간 이후에 코드를 실행시킬 수도 있다.
우리가 publisher, subscriber, subject를 공부할 때 scheduler에 대한 코드는 전혀 없었다.
그 이유는 combine에서는 따로 scheduler를 지정하지 않더라도 기본 scheduler를 제공하기 때문이다.
scheduler는 element가 생성된 스레드와 동일한 스레드를 사용한다.
let subject = PassthroughSubject<Int, Never>()
let token = subject.sink(receiveValue: { value in
print(Thread.isMainThread) // true
})
subject.send(1)
기본적으로 main thread에서 일어나기 때문에 true가 출력된다.
그렇다면 background 스레드(global) 에서 element를 발행(send를 하는 곳)하면 background 스레드에서 element를 받는지를 확인해보자
let subject = PassthroughSubject<Int, Never>()
// 1
let token = subject.sink(receiveValue: { value in
print(Thread.isMainThread)
})
// 2
subject.send(1)
// 3
DispatchQueue.global().async {
subject.send(2)
}
// true
// false
2번째에서 subject에 그냥 send를 보내면 true가 출력된다.
3번째에서 global에 감싸 send를 보내게 되면 false가 출력된다.
이렇게 Combine의 기본 Scheduler를 봤으니 Combine에서 어떻게 Scheduler를 명시적으로 지정해주는지 보도록 하자.
- receive(on: )
- subscribe(on: )
receive(on: )
먼저 receive(on: )을 보도록 하자.
receive(on: )은 publisher로 부터 element를 수신한 scheduler를 지정하는 역할을 한다.
정확히 말하면 downstream의 실행 컨텍스트를 변경하는 역할을 한다.
receive(on: ) 예제
let publisher = ["Zedd"].publisher
publisher
.map { _ in print(Thread.isMainThread) } // true
.receive(on: DispatchQueue.global())
.map { print(Thread.isMainThread) } // false
.sink { print(Thread.isMainThread) } // false
앞서 receive(on: )은 "publisher로 부터 element를 수신할 scheduler를 지정하는 역할"이라고 했다.
원래 기본적으로 element가 생성된 스레드가 MainThread여서 첫번째 map의 결과로 true가 출력된다.
그 후 receive(on: )을 활용해 global 스레드로 변경해주었고, 그 결과 두번째 map의 결과로는 false가 출력된다.
*참고
receive(on: ), subscribe(on: ) 은 파라미터 타입으로 Scheduler 타입을 받는다.
Scheduler 타입의 종류에는 DispatchQueue, OperationQueue, RunLoop, ImmediateScheduler가 있다.
subscribe(on: )
subscribe(on: )은 subsribe, cancel, request operation을 수행할 scheduler를 지정하는 역할을 한다.
위에서 언급했듯이, upstream의 실행 컨텍스트를 변경한다.
(일단 회사 코드에서 subscribe(on: )은 사용을 잘 안해서 예제는 생략하겠다. 그리고 보니까 receive랑 별 차이를 모르겠달까..)
참고링크:
https://zeddios.tistory.com/972
handleevents와 sink의 차이 참고링크:
https://sujinnaljin.medium.com/combine-debugging-2c045cdd2ce3
'iOS 개발 > Combine' 카테고리의 다른 글
[Combine] Operater란? (1) (1) | 2023.05.25 |
---|---|
[Combine] Publisher Future와 CompletionHandler의 차이 (0) | 2023.05.24 |
[Combine] CurrentValueSubject & PassthroughSubject 예제(2 - 1) (0) | 2023.05.18 |
[Combine] Subject 정의 및 예제(2) (0) | 2023.05.18 |
[Combine] Publisher & Subscriber란?(1) (0) | 2023.05.16 |