apple/RxSwift, ReactorKit

RxSwift 스터디 4주차 Subject

lgvv 2021. 7. 10. 02:18

RxSwift 스터디 4주차 Subject

 

이번주는 스트림인 Subject에 대해서 학습.

좀 많이 어려운 부분이 있는데 예전에 멀티미디어 수업에서 오디오 생성자와 소비자를 버퍼를 두고 구현하여 처리한 것을 기반으로 이해.

 

 

 

목차

1. Subjects란?

2. Subjects의 4가지 종류와 Variable

 

 

기본개념 리캡

공식문서에 따르면 아래와 같이 정의되어 있음.

 

"ReactiveX에서 observer는 observable 을 구독한다. observer는 observable이 방출하는 모든 아이템(들)에 대해 반응한다"

 

 

 

Subjects란 무엇일까?


Rx 에서 Subject는 Observable 과 Observer 둘 다 될 수 있는 특별한 형태.

Subject는 Observables을 subscribe(구독) 할 수 있고 다시 emit(방출)할 수 도 있음.

혹은 새로운 Observable을 emit 할 수 있음.


그렇다면 Observable과 Subject의 차이는?

Subscribe 방식의 차이

 


Subjects의 4가지 종류와 Variable


1. PublishSubject

  • 아무것도 없는 빈 상태로 subscribe를 시작, 오직 새로운 elements 만 subscriber에게 emit 시킴.
  • 아래 그림에서 보다시피 발행 이후에 구독했다면 그 이전의 데이터는 받을 수 없음.

Publish Subject

 

샘플코드

  • 우리가 subscribe를 하면 예상한 결과로는 idx로 1, 2, 4가 출력되어야 하는데 1, 2만 출력됨.
  • subscribe가 stop event(complete or error)을 받는다면, future subscribers에도 영향을 미쳐서 값 자체가 만들어지지 않음.
    • 즉,  first subscribe가 stop event를 받게 되면 이것에 대한 영향이 second subscribe까지 작용
  • 그렇다면 위에 있는 주석처럼 만약 complete 부분이 빠지면 다른 결과를 보이는 것을 알 수 있다.
  • 즉, 스트림이 종료되면 해당 스트림은 다시 create해야 한다는 의미

 

        var publishSubject = PublishSubject<String>() // 선언부 
        print(" ==== publish Subject ==== ")
        publishSubject.onNext("publish -> idx 0")
        
        // first subscribe
        publishSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        
        publishSubject.onNext("publish -> idx 1")
        publishSubject.onNext("publish -> idx 2")
        publishSubject.onCompleted() // 만약 이 부분이 빠지면 결과값은 어떻게 될까?
        publishSubject.onNext("publish -> idx 3")
        
        // second subscribe
        publishSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        publishSubject.onNext("publish -> idx 4")
        publishSubject.onCompleted()
        
// 출력값
next(publush -> idx 1)
next(publush -> idx 2)
completed
completed

// 출력값 - 포스팅의 line 12이 없어진다면
next(publish -> idx 1) // first subscribe의 결과
next(publish -> idx 2) // first subscribe의 결과
next(publish -> idx 3) // first subscribe의 결과
next(publish -> idx 4) // first subscribe의 결과
next(publish -> idx 4) // second subscribe의 결과
completed
completed

 

 

 

2. BehaviorSubject

  • 초기화 값을 가진 상태로 시작하는 것이 Publish Subject와의 차이점.
  • 초기값을 방출하거나, 가장 최신의 (가장 늦은) element들을 새 subscribers에게 방출

 

Behavior Subject

 

 

샘플 코드

  • 얘는 사실상 public Subject와 거의 비슷하지만, behavior의 경우에는 처음에 초기화를 해주는 부분이 필요
  • 그리고 가장 최근의 값을 처음 subscribe시에 함께 보냄.
  • 이전과 동일하게 future subscribe에 영향을 미치는 것을 확인.
  • 또한 subscribe전에 갖고 있던 값을 먼저 보여주는 작업 이후에 onNext의 값을 보내줌.
  • 이전 값에 대한 정보로 인해 나타나는 결과값에는 prev로 표현

 

        var behaviorSubject = BehaviorSubject(value: "hehavior -> init") // 선언부
        print(" ==== behavior Subject ==== ")
        
        behaviorSubject.onNext("behavior -> idx 0")
        // first subscribe
        behaviorSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        behaviorSubject.onNext("behavior -> idx 1")
        behaviorSubject.onNext("behavior -> idx 2")
        //behaviorSubject.onCompleted() // 만약 이 부분이 빠지면 결과값은 어떻게 될까?
        behaviorSubject.onNext("behavior -> idx 3")
        
        // second subscribe
        behaviorSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        behaviorSubject.onNext("behavior -> idx 4")
        behaviorSubject.onCompleted()
        
// 결과값
next(behavior -> idx 0)
next(behavior -> idx 1)
next(behavior -> idx 2)
completed
completed

// 결과값 - 포스팅의 라인 11 사라졌을 때의 결과값
next(behavior -> idx 0) // first subscribe - prev
next(behavior -> idx 1) // first subscribe
next(behavior -> idx 2) // first subscribe
next(behavior -> idx 3) // first subscribe
next(behavior -> idx 3) // second subscribe - prev
next(behavior -> idx 4) // first subscribe
next(behavior -> idx 4) // second subscribe
completed
completed

 

 

 

3. ReplaySubject

  • 초기화 된 buffer size로 시작.
  • 그 사이즈까지 buffer의 원소들을 유지하며 새로운 subscriber들에게 방출.

Replay Subject

 

샘플코드

  • 여기의 경우에는 이전에 갖고 있던 값을 모두 보여줌.
  • 다만, 아전의 값들은 버퍼로 지정해준 갯수만큼만 최신 정보를 우선적으로 하여 보여줌.
  • 이번에도 공통적으로 확인할 수 있는 것이 중간에 stop event가 있으면 subscribe를 해도 onNext로 데이터를 받아올 수 없음
  • 또한 버퍼에는 가장 최신의 정보가 FIFO(First In First Out)의 형태로 담김.

 

 

        var replaySubject = ReplaySubject<String>.create(bufferSize: 2)  
        print(" ==== replay Subject ==== ")
        replaySubject.onNext("replay -> idx 0")
        // first subscribe
        replaySubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        replaySubject.onNext("replay -> idx 1")
        replaySubject.onNext("replay -> idx 2")
        replaySubject.onCompleted() // 만약 이 부분이 빠지면 결과값은 어떻게 될까?
        replaySubject.onNext("replay -> idx 3")
        
        // second subscribe
        replaySubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        replaySubject.onNext("replay -> idx 4")
        replaySubject.onCompleted()
        
        
// 결과값
next(replay -> idx 0) // first subscribe - buffer
next(replay -> idx 1) // first subscribe 
next(replay -> idx 2) // first subscribe
completed
next(replay -> idx 1) // second subscribe - buffer
next(replay -> idx 2) // second subscribe - buffer
completed

// 결과값 - 이 포스팅에서 라인 10이 사라진다면?
next(replay -> idx 0) // first subscribe - buffer
next(replay -> idx 1) // first subscribe
next(replay -> idx 2) // first subscribe
next(replay -> idx 3) // first subscribe
next(replay -> idx 2) // second subscribe - buffer
next(replay -> idx 3) // second subscribe - buffer
next(replay -> idx 4) // first subscribe
next(replay -> idx 4) // second subscribe 
completed
completed

 

 

 

4. AsyncSubject 

  • 오직 마지막 값만 방출

async Subject



샘플코드

 

  • 마지막 밸류만 방출.
  • 얘도 마찬가지고 stop event가 future subscribe에 영향

 

        var asyncSubject = AsyncSubject<String>()
        print(" ==== async Subject ==== ")
        asyncSubject.onNext("async -> idx 0")
        
        // first subscribe
        asyncSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        
        asyncSubject.onNext("async -> idx 1")
        asyncSubject.onNext("async -> idx 2")
        asyncSubject.onCompleted() // 만약 이 부분이 빠지면 결과값은 어떻게 될까?
        asyncSubject.onNext("async -> idx 3")
        
        // second subscribe
        asyncSubject.subscribe{ event in
            print(event)
        }.disposed(by: disposeBag)
        asyncSubject.onNext("async -> idx 4")
        asyncSubject.onCompleted()
        
// 결과값
next(async -> idx 2)
completed
next(async -> idx 2)
completed

// 결과값 - 포스팅 코드의 라인 12가 빠졌을 경우
next(async -> idx 4) // first subscribe
next(async -> idx 4) // second subscribe
completed
completed

 

 

 

5. Variable

 

  • Variable은 Behavior Subject를 래핑하고, 현재의 값을 상태로 저장
  • 초기값 또는 가장 최신의 값만 새로운 subscribers에게 방출

 

이건 공식문서의 Subject쪽에 명시되어 있지 않은데, 더이상 사용되지 않음.

 

[DEPRECATED] `Variable` is planned for future deprecation. Please consider `BehaviorRelay` as a replacement. Read more at: