RxSwift 공부합시당: 3. Subjects

한담곰·2024년 10월 28일

쏘의 Swift_Study

목록 보기
12/17

Reference Post
naljin
김종권의 iOS 앱 개발 알아가기

1. Subjects?

  • 런타임 시에 Observable에 값을 추가하여 emit이 발생하게끔 해 주는 대리인
  • Observable에 값을 추가하는 대상은 Observer라고 부름
  • Observable과 Observer 기능을 둘 다 하는 것: Subjects
  • subject와 subscribe의 차이가 있다면, subject는 multicast 방식이기 때문에 여러 개의 observer를 구독할 수 있고, 단순 observable은 unicast 방식이기 때문에 하나의 obserber를 구독할 수 있음.

ex) PublishSuject는 일종의 출판사와 같은 작업
-> 출판사에서 "Is Anyone Listening?"이란 내용을 받음 (.onNext)
-> 구독 (.subscribe)
-> 현재 벌어진 일만 emit. (.subscribe 했을 경우 "Is Anyone Listening?"은 과거에 발생했던 일이라 emit하지 않음.)
-> 뒤에 1과 2 이벤트 추가 시 (현재 벌어진 일), 이것들은 emit

example(of: "PublishSubject") {
	let subject = PublishSubject<String>()
    subject.onNext("Is Anyone Listening?")
    let subscriptionOne = subject
    	.subscribe(onNext: { string in
        	print(string)
        })
    subejct.onNext("1") // print: 1
    subject.onNext("2") // print: 2
}

2. Subject의 종류

(1) PublishSubject

  • empty 상태로 시작하여 새로운 이벤트만 subscriber(구독자)에게 emit
  • 구독된 순간 새로운 이벤트 수신을 알리고 싶을 때 사용!

    첫번째 가로줄: subjects 배포
    두세번째 가로줄: subscriber(구독자)
    아래로 향하는 점선 화살표: 이벤트 emit(방출)
    위로 향하는 점선 화살표: 구독 요청

예제 코드

example(of: "PublishSubject") {
	let subject = PublishSubject<String>()
    subject.onNext("Is Anyone Listening?")
    
    // 첫 번째 구독 요청
    let subscription1 = subject
    	.subscribe(onNext: { string in 
        	print(string)
        })
    subject.onNext("1")
    subject.onNext("2")
    // prints : 1, 2
    
    // 두 번째 구독 요청
    
    let subscription2 = subject
    	.subscribe({ event in
        	print("2)", event.element ?? event)
        })
    subject.onNext("3") // print: 2) 3
    subscription1.dispose()
    subject.onNext("4") // print: 2) 4
    subject.onCompleted() // print: 2) completed
    subject.onNext("5") // none of print
    subscription2.dispose()
    
    let disposeBag = DisposeBag()
    
    subject.
    	subscribe {
        	print("3)", $.element, ?? $0) // print: 3) completed
        }
        .disposed(by: disposeBag)
    }

☑️ PublishSubject는 현재 구독 요청한 대상만 관심을 갖음 -> subejct.onNext("3")에서 subscriptoin1 관련된 호출은 하지 않음!
☑️ 이벤트를 추가할 땐, subject. 으로 접근하여 추가! (onNext, on)
☑️ subejct.onCompleted() 할 시, 구독 종료..~

🚨subscrbier에게 이벤트를 방출(emit)시키는 방법

  • 기존 구독자가 있을 때 subject.onNext()한 경우
  • 새로운 구독자가 구독(subscribe) 한 경우

🤔 PublishSubject 사용은 언제 하면 좋을까?

  • 시간에 민감한 데이터를 모델링 할 경우. 예를 들면 실시간 경매 앱! 10:00 am 경매 시작일 경우, 10:01 am에 접속했을 땐 알림이 보내질 필요가 없는 경우 등등

(2) BehaviorSubject

  • 하나의 초기값으로 시작하여 최신 값(.next)만 새로운 구독자(subscriber)에게 이벤트 방출(emit)

    첫번째 가로줄: subscribe
    두세번째 사로줄: subscriber
    --> 첫번째 이벤트가 발생한 후 첫번째 구독자가 구독을 시작했지만, 1을 받음 (PublishSubject였다면 못 받음!!)
    --> 두번째 이벤트가 발생한 후 두번째 구독자가 구독을 시작했지만 2를 받음

예제코드

enum MyError: Error {
	case anError
}

examplease(of: "BehaivorSubject") {
	// BehaviorSubject는 초기값이 필수🌟이므로 초기값 삽입
    let subject = BehaviorSubject(value: "초기값")
    let disposeBag = DisposeBag()
    
    subject.onNext("X")
    subejct
    	.subscrbie { print("1)", $0)
        }
        .disposeBag(by: disposeBag)
    // print: 1) next(X)
    
    subject.onError(MyError.anError)
    // print: 1) error(anError)
    
    subject
    	.subscribe { print("2)". $0)
    } 
    .disposed(by: disposeBag)
    // print: 2) error(anError)
}

☑️BehaviorSubject는 초기화 값이 필수이며, 항상 저장되어 있는 최신값을 방출(emit)

🤔 BehaviorSubject는 언제 사용하면 좋을까?

  • 뷰를 가장 최신의 데이터로 미리 채우기에 용이하다! (예를 들면, 유저 프로필 화면을 BehaviorSubject에 바인딩)

(3) ReplaySubject

  • 버퍼 사이즈를 지정하며, 버퍼 사이즈만큼 새로운 구독자에게 이벤트 방출

    첫번째 가로 줄: subscribe
    두세번째 가로 줄: subscriber
    첫 subscriber가 구독 신청을 하면 최대 2개를 emit받음 (버퍼 사이즈가 2인 경우)
    두번째 subscriber가 구독 신청을 하면 최대 2개를 emit받음 (버퍼 사이즈가 2인 경우)

🚨버퍼 사이즈는 메모리에 할당되므로 버퍼 사이즈가 커질수록 메모리에 큰 부하를 갖는 것을 주의하자🚨

예제 코드

example(of: "ReplaySubject") { 
	let subject = ReplaySubject<String>.create(bufferSize: 2)
    let disposeBag = DisposeBag()
    
    subject.onNext("1")
    subject.onNext("2")
    subject.onNext("3")
    
    subject
    	.subscribe {
        	print("1)", $0)
        }
        .disposed(by: disposeBag)
    /*
    prints
    1) next(2)
    1) next(3)
    */
    
subject
    	.subscribe {
        	print("2)", $0)
        }
        .disposed(by: disposeBag)
    /*
    prints
    2) next(2)
    2) next(3)
    */
    
    subject.onNext("4")
     /*
    prints
    1) next(4)
    2) next(4)
    */
    
    subject.onError(MyError.anError)
     /*
    prints
    2) error(anError)
    2) error(anError)
    */
    
    // subejct.dispose()
    
    subject.subscribe {
    	print("3)", $0)
    }
    .disposed(by: disposeBag)
     /*
    prints
    3) next(3)
    3) next(4)
    3) error(anError)
    */

☑️ 버퍼 사이즈가 2이므로 구독하면 항상 2개씩 emit
☑️ subject.onError() 해도 print(3, $0) 부분에서 next(3), next(4) 출력됨.. -> subject.onError() 후에 subject.dispose()를 넣으면 해결됨

🤔 ReplaySubject는 언제 사용하면 좋을까?

  • 최신 데이터 여러 개를 보여 주고 싶은 경우에! (예를 들면, 최근 검색 5개 현황을 보여 주고 싶을 때)

(4) PublishRelay, BehaviorRelay

  • 오직 .next 이벤트만 emit함. (.completed, .error 무시! non-terminating에서 유용하게 사용)
  • PublishRelay: PublishSubject를 단순히 wrap 한 것이며, .next만 가능하고 기능은 동일.
  • BehaviorRelay: BehaviorSubject를 단순히 wrap 한 것이며, .next만 가능하고 기능은 동일.
    🚨 relay의 핵심은 절대 끝나지 않음을 보장하는 것이다

PublishRelay 예제 코드

example(of: "PublishRelay:) {
	let relay = PublishRelay<String>()
    let disposeBag = DisposeBag()
    
    relay.accept("똑 똑, 계시나요?")
    relay
    	.subscribe(onNext: { print($) 
        })
        .disposed(by: disposeBag)
    relay.accept("1") // print: 1

☑️ relay의 추가는 .accept() 로 접근
☑️ onError, onCompleted 사용 불가

BehaviorRelay 예제 코드

example(of: "BehaviorRelay") {
	let relay = BehaviorRelay(value: "초기값")
    let disposeBag = DisposeBag()
    
    realy.accept("새로운 값")
    relay
    	.subscribe { print("1)", $0)
        }
        .disposed(by: disposeBag)
    // print: 1) next("새로운 값")
    
    relay.accept("1")
    // print: 1) next(1)
    
    relay
    	.subscribe { print("2)", $0) 
        }
        .disposed(by: disposeBag)
    // print: 2) next(1)
    
    relay.accept("2")
    // print: 2) next(2)

☑️ .accept()로 다가가며, onError, onCompleted 사용 불가.

(5) AysncSubject

  • 마지막의 이벤트만 구독자에게 emit (단, .completed이벤트 받은 경우에만)
  • onNext() 메소드 사용 불가. 오직 accept(:_)만 사용 가능!!
profile
i♡S Developer

0개의 댓글