[iOS | RxSwift] Subject

minji0801·2022년 1월 22일
0

RxSwift

목록 보기
4/6
post-thumbnail

개요

이번에는 RxSwift의 Subject에 대해서 알아볼 것이다.
Subject는 무엇이고, 어떻게 만들어 사용하는 건지 알아보자.

👀 Subject란?

이전에 Observable에 대해서 배우긴 했지만, 실제 필요한 것은 실시간으로 Observable에 값을 추가하고 Subscribe이 방출하도록 하는 것이다.

즉, Subject는 "Observable 이자 Observer"이다.

Subject의 종류

  • PublishSubject
    빈 상태로 시작하여 새로운 값만 subscriber에 방출하는데, 구독된 순간 새로운 이벤트 수신을 알릴 때 용이하다.

    첫 번째 subscriber는 구독한 이후에 처음으로 발생한 2, 3을 받는다.
    두 번째 subscriber는 1,2가 방출된 다음에 구독했기 때문에 구독한 이후에 처음 발생한 3을 받는다.

  • BehaviorSubject
    하나의 초깃값을 가진 상태로 시작하여 새로운 subscriber에게 초깃값 또는 최신값을 방출한다.

    마지막 Next 이벤트를 새로운 구독자에게 넘겨주는 것 외에 PublishSubject와 동일하다.

  • ReplaySubject
    버퍼를 두고 초기화하며 버퍼 사이즈만큼 값을 유지하면서 새로운 subscriber에게 방출한다.

    위에서 ReplaySubject 버퍼 사이즈를 2라고 두었다.
    첫 번째 subscriber는 처음부터 구독하기 때문에 그대로 다 받았고, 두 번째 subscriber는 뒤늦게 구독했음에도 버퍼 사이즈만큼 값을 받은 것이다.

여기서 주의해야 할 점은 버퍼는 메모리를 차지하기 때문에 이미지나 배열 등 메모리를 크게 차지하는 형태로 버퍼를 갖는 것은 좋지 않다.


💻 Subject 만들기

PublishSubject

let publishSubject = PublishSubject<String>()

publishSubject.asObserver().onNext("1")

let subscriber1 = publishSubject    // subscriber1 구독 전 1은 방출되지 않음
    .subscribe(onNext: {
        print("subscriber1 : \($0)")
    })

publishSubject.onNext("2")      // 방출
publishSubject.on(.next("3"))   // 방출

subscriber1.dispose()   // subscriber1 종료

let subscriber2 = publishSubject    // subscriber2 구독 전 1,2,3은 방출되지 않음
    .subscribe(onNext: {
        print("subscriber2 : \($0)")
    })

publishSubject.onNext("4")      // 방출
publishSubject.onCompleted()    // Subject 종료

publishSubject.onNext("5")  // Subject 종료되었으므로 방출되지 않음

subscriber2.dispose()   // subscriber2 종료

// print
// subscriber1 : 2
// subscriber1 : 3
// subscriber2 : 4

▼ MarbleDiagram

BehaviorSubject

let disposeBag = DisposeBag()

enum SubjectError: Error {
    case error1
}

let behaviorSubject = BehaviorSubject<String>(value: "0")

behaviorSubject.onNext("1")

behaviorSubject.subscribe { // 1 이후에 구독했지만, 1 전달 받음
    print("subscriber1 : \($0)")
}
.disposed(by: disposeBag)

behaviorSubject.onError(SubjectError.error1)

behaviorSubject.subscribe { // error 이후에 구독했지만, error 전달 받음
    print("subscriber2 : \($0)")
}
.disposed(by: disposeBag)

let value = try? behaviorSubject.value()    // 가장 최신 값을 뽑아냄
print(value)

// print
// subscriber1 : next(1)
// subscriber1 : error(error1)
// subscriber2 : error(error1)
// nil

▼ MarbleDiagram

ReplaySubject

let replaySubject = ReplaySubject<String>.create(bufferSize: 2)

replaySubject.onNext("1")
replaySubject.onNext("2")
replaySubject.onNext("3")

replaySubject.subscribe {   // 1,2,3 이후에 구독했지만 버퍼사이즈 2만큼인 2와 3을 받음
    print("subscriber1 : \($0)")
}
.disposed(by: disposeBag)

replaySubject.subscribe {   // subscriber1과 동일
    print("subscriber2 : \($0)")
}
.disposed(by: disposeBag)

replaySubject.onNext("4")
replaySubject.onError(SubjectError.error1)
replaySubject.dispose() // Subject 종료

replaySubject.subscribe {   // Subject 종료 후 구독했으므로 에러 방출
    print("subscriber3 : \($0)")
}
.disposed(by: disposeBag)

// print
// subscriber1 : next(2)
// subscriber1 : next(3)
// subscriber2 : next(2)
// subscriber2 : next(3)
// subscriber1 : next(4)
// subscriber2 : next(4)
// subscriber1 : error(error1)
// subscriber2 : error(error1)
// subscriber3 : error(Object `RxSwift.(unknown context at $105b9aef0).ReplayMany<Swift.String>` was already disposed.)

▼ MarbleDiagram


마무리

이렇게 총 3종류의 Subject를 알아보았다.
Observable에 대해서는 이렇게 마무리하고, 다음부터는 Operator(Filtering Operator)를 파헤쳐 보자.

profile
iOS Developer

0개의 댓글