study record

[RxSwift] Subject의 종류 본문

iOS/RxSwift

[RxSwift] Subject의 종류

asong 2022. 3. 11. 18:11

Subject

Subject는 Observable이자 Observer 두 역할 모두를 수행한다. next 이벤트를 통해 값을 전달받고, 이벤트를 수신할 때마다 subscriber를 통해 방출한다.

 

Subject 종류

  • PublishSubject : 빈 상태로 시작하여 새로운 값만을 subscriber에 방출
  • BehaviorSubject : 하나의 초기값을 가진 상태로 시작하여, 새로운 subscriber에게 초기값 또는 최신값을 방출
  • ReplaySubject : 버퍼를 두고 초기화하며, 버퍼 사이즈 만큼의 값들을 유지하면서 새로운 subscriber에게 방출
  • Variable : BehaviorSubject를 래핑하고, 현재의 값을 상태로 보존. 가장 최신/초기 값만을 새로운 subscriber에게 방출

 

PublishSubject

Subject로 전달되는 이벤트를 Observer에게 전달하는 가장 기본적인 형태의 Subject.

빈 상태에서 시작하고, 새로운 값을 전달받을 때에 subscriber를 통해 방출한다.

구독한 시점 이후부터의 아이템을 전달받는 것을 볼 수 있다.

 

- 언제 사용하나?

구독된 순간 새로운 이벤트 수신을 알리고 싶을 때

 

시간이 민감한 데이터를 모델링할 때 유용하다. 예를 들어 실시간 경매 앱이 존재한다고 했을 때 경매 시간이 끝난 이후에 들어온 유저에게 "경매 시간이 얼마남지 않았습니다!"와 같은 메세지를 전달하는 것은 매우 무의미하다. 시간이 지나 구독을 시작한 Subscriber에게는 이벤트를 전달하지 않으므로, 무의미한 이벤트 전달을 하지 않을 수 있다.

let subject = PublishSubject<Int>()
// subject에 .next 이벤트 방출
subject.onNext(1)

// subject 구독
let subscribeOne = subject.subscribe(onNext: {(value) in
    print(value)
})

// .next 이벤트 방출
subject.onNext(2)

// 구독취소
subscribeOne.dispose()

// 2

1은 출력되지 않는다. 구독 이후부터 이벤트가 받아지면 방출되기 때문이다. 

 

BehaviorSubject

BejaviorSubject와 다르게 하나의 초기값을 가지고 있어야 한다. 

초기값을 가지고 있는 상태이므로 초기값으로 next 이벤트로 값이 전달되면서 시작된다.

새롭게 구독을 시작하면 그 직전의 값을 전달받고 시작한다.

 

- 언제 사용하는가?

뷰를 가장 최신의 데이터로 미리 세팅할 때

 

예를들면 어떤 이미지를 호출해주는 BehaviorSubject가 있고 해당 BehaviorSubject를 이미지뷰와 바인딩 시켰을 때 가장 최근에 불려왔었던 이미지를 미리 채워둠으로서 로딩하는 모습을 감추고 싶다하는 시나리오에 매우 유용하게 사용될 수 있다.

 

let subject = BehaviorSubject(value: 1)
let disposebag = DisposeBag()

subject.subscribe{ (value) in
    print(value)
}.disposed(by: disposebag)

subject.onNext(2)

// next(1)
// next(2)

 

 

ReplaySubject

하나 이상의 새로운 이벤트를 버퍼에 저장하고 Observer가 구독을 시작하면 버퍼에 있는 모든 이벤트를 전달한다.

버퍼의 크기를 설정할 수 있고, 버퍼 크기가 0이면 PublishSubject와 동일하다.

 

let disposeBag = DisposeBag()
        
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
replaySubject.onNext("1")
replaySubject.onNext("2")
replaySubject.onNext("3")
        
replaySubject
    .subscribe {
        print("첫번째 구독자 \($0)")
    }
    .disposed(by: disposeBag)
        
replaySubject
    .subscribe {
        print("두번째 구독자 \($0)")
    }
    .disposed(by: disposeBag)
    
// print
첫번째 구독자 next(2)
첫번째 구독자 next(3)
두번째 구독자 next(2)
두번째 구독자 next(3)

- 언제 사용하는가?

최근 값 1개 외에 더 많은 값을 불러오고자 할 때

 

버퍼들은 메모리가 가지고 있으며, 데이터 타입이 메모리를 크게 차지하는 값(이미지나 배열같은)이라면 메모리 부하가 날 수 있다.

 

Variable

BehaviorSubject를 래핑하고 있으며, 현재 값을 상태로 가지고 있다.

complete, error 이벤트가 발생하지 않으며 variable이 해제될 때 자동으로 complete된다.

 

새로운 요소를 추가하기 위해 onNext를 사용할 수 없다.

variable은 상태 값이므로 에러가 발생하지 않는 것을 보장하여 error이벤트를 발생시킬 수 없다.

variable이 해제될 때 자동으로 complete되기 때문에 complete 이벤트를 발생시킬 수 없다.

 

- 언제 사용하는가?

현재 값을 확인하고 싶을 때

 

let variable = Variable("Initial value")
let disposebag = DisposeBag()

variable.value = "New value"

variable.asObservable().subscribe{ (value) in
    print("1) \(value)")
}.disposed(by: disposebag)

variable.value = "Set other value"

variable.asObservable().subscribe{ (value) in
    print("2) \(value)")
}.disposed(by: disposebag)

variable.value = "Set other value2"

// 1) next(New value)
// 1) next(Set other value)
// 2) next(Set other value)
// 1) next(Set other value2)
// 2) next(Set other value2)

 

 

참고 : 

https://gwangyonglee.tistory.com/61

https://velog.io/@chagmn/RxSwift-Subjects

https://www.notion.so/BehaviorSubject-a3ab38e952134b43ad4b2bdbcaa73fd4