ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Rxswift] 03.Subjects
    iOS/RxSwift 2021. 12. 19. 21:03

    이 게시글은 raywenderlich, RxSwift: Reactive Programming with Swift 책을 참고하여 작성한 글입니다.

    observerble은 RxSwift에서 필수적이지만, read-only이다.

    할 수 있는것은, subscribe하고 그들이 생산하는 새로운 event를 알림받는것이다.

    observable과 observer의 역할을 모두 할 수 있는 것이 있는데, 바로 Subject 이다.

    소개

    example(of: "PublishSubject") {
      let subject = PublishSubject<String>()
    
      // 1
        subject.on(.next("Is anyone listening?"))
    
      // 2
        let subscriptionOne = subject
            .subscribe(onNext: { string in
                print(string)
        })
    
      // 3
        subject.on(.next("1"))
      subject.onNext("2")
    }
    1. subject에서 “Is anyone listening?”을 emit(publish)한다.
      • 하지만 observer가 없기 때문에 아무것도 일어나지 않는다.
    2. subscriptionOne이 subject를 subscribe한다.
      • 역시나 아무 일도 일어나지 않는다.
      • PublishSubject 는 emit하는 시점에 존재하는 subscriber에게만 emit하기 때문이다.
    3. subject에서 “1”, “2”을 emit(publish)한다.
      • subscriptionOne이 현 시점에서 subject를 subscribe하고있기 때문에 event가 제대로 전달된다.
      • 따라서 아래와 같은 결과가 나온다.
    --- Example of: PublishSubject ---
    1
    2

    종류

    • PublishSubject, BehaviorSubject, ReplaySubject, AsyncSubject 총 4개의 subject type이 있다.
    • 또한, RxSwift는 Relay라는 개념을 제공한다.
      • PublishRelay, BehaviorRelay 총 2개가 있는데, 이들은 모두 각각의 subject를 감싼 형태로 볼 수 있다.
      • next event 만 relay, accept 할 수 있다.
      • 따라서 completed or error event를 추가할 수 없고, non-termination sequence와 잘 어울린다.

    💡 원래, realy는 RxCocoa의 일부로서 역할을 했다.
    하지만 realy가 일반적으로 잘 사용되는 개념이기도 하고, Cocoa가 아닌 환경에서도 유용했다.
    따라서 새로운 모듈로 분리되었다. RxCocoa는 이 새로운 모듈인 RxRealy에 의존한다.

    PublishSubject

    • 해당 시점에 존재하는 subscriber에게 새로운 event를 알리고 싶을 때 사용된다.
    • subscriber가 unsubscribe하거나, subject가 complete or error event로 terminat됐을 때 까지 지속된다.
    • marble diagram으로 확인해보면 아래와 같다.

    💡 맨 윗줄은 publish subject의 event를 나타내고, 그 아랫줄 부터는 subscriber를 나타낸다.
    위로 향하는 화살표는 subscribe를 한 시점을 나타낸다.
    아래로 향하는 화살표는 emit event를 나타낸다.

    • 첫 번째 subscriber은 1 이 emit된 이후에 추가되었으므로 23 에 대해서만 전달받는다.
    • 두 번째 subscriber은 2 가 emit된 이후에 추가되었으므로 3 에 대해서만 전달받는다.
    1) 2
    1) 3
    2) 3
    • stop event라고 불리는 completed or error event 이후에는, 새로운 subscriber에게 더 이상 next event 가 전달되지 않고 stop event 가 전달된다.
    • 예시
      • 온라인 초대 서비스와 같은 time-sensitive data 를 다룰 때 publish subject가 유용하다.
        • 10:01에 접속한 사용자가 “9:59에 서비스가 1분 남았다는 알림”을 받을 일이 없기 때문이다.
      • Publish subject는 새로운 subscriber에게 event를 반복하지 않기 때문에 아래와 같은경우에서 유용하다.
        • “유저가 무언가를 탭했다는 이벤트”, 또는 “방금 도착한 알림 이벤트”

    BehaviorSubject

    • publish subject와 비슷하지만, 새로운 subscriber에게 가장 최근의 event를 replay한다는 점에서 다르다.
    • marble diagram으로 보면 아래와 같다.

    💡 BehaviorSubject 는 항상 가장 마지막의 element를 emit하기 때문에, 초깃값을 설정해주어야 한다.
    초깃값을 설정할 수 없다면, PublishSubject 를 사용하건, Optional 하게 모델링 해야한다.

    • 예시
      • 가장 최근의 data를 가지고 view의 초기데이터를 설정할 때 유용하다.
        • 유저 프로필 화면을 behavior subject로 연결해 가장 최신의 데이터를 설정할 수 있다.
      • 새 subscriber에게 최근의 value를 전달하기 때문에 다음과 같은 상황에서 유용하다.
        • “request is currently loading”, “the time is now 9:41” 의 state를 모델링할 때

    ReplaySubject

    • 가장 최근의 event를 선택한 size만큼 cache, buffer 한뒤, 새로운 subscription이 발생했을 때 해당 subscriber에게 buffer에 있는 모든 event를 emit한다.
    • buffer size가 2일 때의 marble diagram은 아래와 같다.
      • 첫 번째 subscriber은 최근의 event가 없기 때문에, 이후에 emit되는 event들만 받는다.
      • 두 번째 subscriber은 그 즉시, subscribe하기 이전에 발생한 두 개의 event를 받는다.
      • 이후에는 마찬가지로, emit되는 event들을 받는다.

    🚨 replay subject를 사용할 때, buffer은 항상 메모리에 존재한다는 것을 명심해야한다. 만약 buffer size를 크게 잡거나, size가 큰 image를 buffer에 넣게 되면 과도한 메모리를 사용하게 된다.

    🚨 또한, replay subject에 array를 담을 때에도 조심해야 한다. 각각의 emit된 element는 array이기 때문에, memery pressure가 발생하기 쉽다.

    • stop event(error, completed)가 발생하기 이전의 buffer*을 계속해서 보유하고 있어서, stop event이후의 subscriber에게 *buffer + stop event를 모두 emit한다.
    • 만약 subscribe이전에 dispose() 가 발생했다면, 아래와 같은 문구가 나오게 된다.
    3) Object `RxSwift...ReplayMany<Swift.String>` was already disposed.
    • dispose bag에 넣는다면 이런 edge case를 피할 수 있다.

    Relay

    • relay는 subject를 그것의 replay하는 행동을 유지하면서 포장한것을 말한다.
    • accept(_:) 메소드를 사용하고, onNext, onError, onCompleted 는 사용할 수 없다.
    • // 21.01.05 추가 )
      Relay는 onComplete, onError가 없기 때문에 끝나는시점이 없다고 볼 수 있다.
      그렇기 때문에 직접 dispose를 구현해주지 않으면 메모리 누수가 발생하기 쉽다.

    PublishRelay

    • 코드를 살펴보자
    example(of: "PublishRelay") {
        let relay = PublishRelay<String>()
    
        let disposeBag = DisposeBag()
    
        // 1
        relay.accept("Knock knock, anyone home?")
    
        // 2
        relay
            .subscribe(onNext: {
                print($0)
            })
            .disposed(by: disposeBag)
    
        // 3
        relay.accept("1")
    }
    • 1에서 새로운 element를 받긴 했지만, PublishSubject와 마찬가지로 당시 subscriber가 없기 때문에 아무것도 발생하지 않는다.
    • 2에서 새로운 subscribe가 추가된 뒤, 3에서 추가적으로 element를 받아 아래와 같은 결과가 나온다.
    --- Example of: PublishRelay ---
    1

    BehaviorRelay

    • BehaviorSubject와 마찬가지로 초깃값이 필요하고, 가장 최신 혹은 초깃값을 새 subscriber에게 반복해서 제공한다.
    • 이것의 특별한 힘은 현재의 값을 언제든 얻을 수 있다는 점이다.
    • 그렇기에 명령형과 반응형 사이의 연결통로가 되어줄 수 있다.
    • 코드를 살펴보자.
    example(of: "BehaviorRelay") {
      // 1
      let relay = BehaviorRelay(value: "Initial value")
      let disposeBag = DisposeBag()
    
      // 2
      relay.accept("New initial value")
    
      // 3
      relay
        .subscribe {
          print(label: "1)", event: $0)
        }
        .disposed(by: disposeBag)
    
        // 4
        relay.accept("1")
    
        // 5
        relay
          .subscribe {
            print(label: "2)", event: $0)
          }
          .disposed(by: disposeBag)
    
        // 6
        relay.accept("2")
    }
    • 1에서 새로운 BehaviorRelay를 생성하는데, 초깃값이 있기 때문에 타입을 추론할 수 있다.
    • 짐작 가능하겠지만 결과는 아래와 같이 나온다.
    --- Example of: BehaviorRelay ---
    1) New initial value
    1) 1
    2) 1
    1) 2
    2) 2
    • 또한 위에서 말했듯 명령형과 반응형의 연결통로가 될 수 있도록, 현재의 값을 확인할 수 있다.
    print(relay.value)
    
    // 결과
    2

    'iOS > RxSwift' 카테고리의 다른 글

    [RxSwift] Error Handling [2]  (0) 2022.01.27
    [RxSwift] Error Handling [1]  (0) 2022.01.25
    RxSwift로 리펙토링 맛보기  (0) 2022.01.03
    [RxSwift] 02.Observables / raw  (0) 2021.12.19
    [RxSwift] 01.Fundamental / raw  (0) 2021.12.19

    댓글

Designed by Tistory.