Publish, Behavior, Replay Subject.
publishSubject
* 이 포스트는 RxSwift 4.3.1, swift 4.2 버전을 기준으로 작성되었습니다.
* RxSwift 의 PublishSubject.swift, BehaviorSubject.swift, ReplaySubject.swift 내용
ReactiveX Observable 에는 "Hot Observable" 과 "Cold Observable"의 개념이 있는데, Subject 는 Cold Observable을 Hot 하게 변형하는 효과를 얻을 수 있다.
Subject 는 Imperative eventing 로 어떤 이벤트를 발생 하고 싶을때. 얼마나 많은 객체에게 그 이벤트을 구독 하는지 중요하지 않다. 원하는 이벤트를 subscription ( observer ) 존재 여부와 관계없이 이벤트를 발행 할 수 있다.
4가지 종류의 subject 가 있다.
Complete 될때까지 이벤트는 발생되지 않으며, complete 가 되면 마지막 이벤트를 발생하고 종료된다.
클래스 선언부
class AsyncSubject<Element>
: Observable<Element>
, SubjectType
, ObserverType
, SynchronizedUnsubscribeType
만약 에러로 종료되면 마지막 이벤트 전달 없이 에러가 발생됩니다.
예제
let asyncSubject = AsyncSubject<String>()
asyncSubject.debug().subscribe{print($0)}.disposed(by: disposeBag)
asyncSubject.on(.next("1"))
asyncSubject.on(.next("2"))
asyncSubject.on(.next("3"))
asyncSubject.on(.completed)
결과
(asyncSubject()) -> subscribed
(asyncSubject()) -> Event next(3)
next(3)
(asyncSubject()) -> Event completed
completed
(asyncSubject()) -> isDisposed
subject의 debug 로그는 빨간색, subscribe 로그는 녹색이다.
"3" 이벤트와 완료 이벤트가 전달된 것을 확인할수 있다.
PublishSubject 는 subscribe 된 시점 이후부터 발생한 이벤트를 전달한다.
subscribe 되기 이전의 이벤트는 전달하지 않는다.
클래스 선언부
class PublishSubject<Element>
: Observable<Element>
, SubjectType
, Cancelable
, ObserverType
, SynchronizedUnsubscribeType
에러가 발생하면 마찬가지로 에러를 전달한다.
예제
let publishSubject = PublishSubject<String>()
publishSubject.debug().subscribe{print("first subscribe :\($0)")}.disposed(by: disposeBag)
publishSubject.on(.next("1"))
publishSubject.on(.next("2"))
publishSubject.debug().subscribe{print("second subscribe :\($0)")}.disposed(by: disposeBag)
publishSubject.on(.next("3"))
publishSubject.on(.completed)
결과
(publishSubject()) -> subscribed
(publishSubject()) -> Event next(1)
first subscribe : next(1)
(publishSubject()) -> Event next(2)
first subscribe : next(2)
(publishSubject()) -> subscribed
(publishSubject()) -> Event next(3)
first subscribe : next(3)
second subscribe : next(3)
(publishSubject()) -> Event completed
first subscribe :completed
(publishSubject()) -> isDisposed
(publishSubject()) -> Event completed
second subscribe :completed
(publishSubject()) -> isDisposed
subject의 debug 로그는 빨간색, subscribe 로그는 녹색이다.
첫번째 subscribe는 이벤트를 발생하기 전부터 구독했고, 두번째 subscribe는 두번째 이벤트 발생이후에 구독했다.
로그를 보면 첫번째는 모든 이벤트, 두번째에는 "3" 만이 전달된 것을 확인할수있다.
이처럼 포인트는 구독이후에 발생하는 이벤트를 받게 되는것
기본적으로는 publish 와 크게 다르지 않지만 초기 값을 가진 subject 이다. subscribe 가 발생하면 즉시 현재 저장된 값을 이벤트로 전달한다. 마지막 이벤트 값을 저장하고 싶을때 사용한다.
클래스 선언부
class BehaviorSubject<Element>
: Observable<Element>
, SubjectType
, ObserverType
, SynchronizedUnsubscribeType
, Disposable
에러가 발생시 마찬가지로 에러를 전달한다.
let behaviorSubject = BehaviorSubject<String>(value: "tom")
behaviorSubject.debug("behavior subject log 1: ").subscribe{print($0)}.disposed(by: disposeBag)
behaviorSubject.on(.next("jack"))
behaviorSubject.on(.next("wade"))
behaviorSubject.debug("behavior subject log 2: ").subscribe{print($0)}.disposed(by: disposeBag)
결과
behavior subject log 1: -> subscribed
behavior subject log 1: -> Event next(tom)
next(tom)
behavior subject log 1: -> Event next(jack)
next(jack)
behavior subject log 1: -> Event next(wade)
next(wade)
behavior subject log 2: -> subscribed
behavior subject log 2: -> Event next(wade)
next(wade)
첫번째 subscribe 직후 최초 생성시 설정한 값인 tom 이 이벤트로 전달됐다.
이후 jack wade 가 전달되었으며, 이 이후 두번째 subscribe 되자, 마지막 값인 wade 가 이벤트로 전달되었다.
포인트는 마지막 이벤트의 값이 저장된다는 것이다.
마지막 값이 중요하거나, 최초 subscribe 시 이벤트가 바로 전달되어야 할때 사용하면 유리하다
n개의 이벤트를 저장하고 subscribe 가 되는 시점과 상관없이 저장된 모든 이벤트를 전달한다.
RxSwift 에서는 create(bufferSize bufferSize: Int) 와 createUnbounded 의 생성함수를 가진다.
createUnbounded 는 Subject 의 생성 이후 발생하는 모든 이벤트를 저장한다.
클래스 선언부 및 생성자
class ReplaySubject<Element>
: Observable<Element>
, SubjectType
, ObserverType
, Disposable
static func create(bufferSize: Int) -> ReplaySubject<Element>
static func createUnbounded() -> ReplaySubject<Element>
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
replaySubject.on(.next("tom"))
replaySubject.on(.next("jack"))
replaySubject.on(.next("wade"))
replaySubject.debug("replay subject log: ").subscribe{print($0)}.disposed(by: disposeBag)
결과
replay subject log: -> subscribed
replay subject log: -> Event next(jack)
next(jack)
replay subject log: -> Event next(wade)
next(wade)
이벤트는 3번 발생했지만, 버퍼 사이즈가 2이기 때문에 jack 과 wade 만 전달되었다.
ReplaySubject 생성 부분을 아래와 같이 바꿔보자.
let replaySubject = ReplaySubject<String>.createUnbounded()
모든 이벤트가 전달 된다.
사용할때는 메모리 관리에 유의 하자!