티스토리 뷰

TIL

[TIL] 2022 / 10 / 26 - Rx

희철 2022. 10. 26. 17:26

Observable vs Observer

 

옵저버블 - 이벤트를 emit

옵저버 - 이벤트 처리

 

옵저버블과 옵저버를 통해 데이터 스트림을 통제할 수 있고, Operator를 통해 조작할 수 있음

 

근데 옵저버블은 이벤트에 대한 처리를 할 수 없고, 옵저버는 이벤트에 대한 처리만 가능함.

-> 그래서 Subject나옴

 

 

Subject

 

위에서 말했듯이 emit과 Subscribe 둘 다 가능.

 

네 가지 종류가 있음

 

Publish, Behavior, Replay, Async

 

근데 UI의 경우는 에러가 날 일이 거의 없음.

 

그래서 핸들링도 딱히 필요없고, 이벤트에 대한 요소도 대부분 infinite

 

RxSwift로도 충분했지만, UI를 더 잘 관리할 수 있는게 RxCocoa

 

 

onNext를 전달할 수 있다는 것은, error와 complete도 전달할 수 있다는 의미

-> 근데 UI에서는 거의 실패할 가능성이 없음

-> 그냥 안쓰고 onNext만 사용하면 되긴하지만 next만 전달하는 요소인 Replay가 있음

 

 

 

Replay

 

UI적인 부분을 만지는데 더 최적화되어있음

 

Publish와 Behavior가 있고 Subject와 거의 유사함

 

차이는 Replay는 Completed와 Error이벤트를 받지 못함

 

만약 UI가 Completed와 Error를 받으면 disposed상태가 되는데, 이렇게 되면 더 이상 next이벤트를 전달받을 수 없음

-> 반응형의 장점이 사라짐

 

즉, Replay를 사용하게되면 Completed와 Error를 받을 수 없다는 얘기이고, 이는 disposed가 되기 전까지 Subscribe가 해제되지 않는다는 것을 의미함

-> 그래서 직접 처리를 해줘야함

 

Subject에서의 next가 Replay에서는 accept.

 

Subject는 Observable과 자주 사용되고, Relay는 driver와 자주 사용이됨(짝꿍)

 

 

 

Subscribe -> bind -> drive

 

버튼을 탭했을때 레이블에 텍스트를 띄우는 코드

 

1. Subscribe 이용

        button.rx.tap
            .subscribe(onNext: { [weak self] _ in
                self?.label.text = "안녕 반가워"
            })
            .disposed(by: disposeBag)

 

2. [weak self] 대신 withUnretained(self) 사용

        button.rx.tap
            .withUnretained(self)
            .subscribe(onNext: { (vc, _) in
                vc.label.text = "안녕 반가워"
            })
            .disposed(by: disposeBag)

 

3.

        button.rx.tap
            .map { }
            .map { }
            .map { } //여기까지는 글로벌쓰레드
            .observe(on: MainScheduler.instance) //이후의 구독하는 것에 대해서는 메인스레드에서 동작하게끔
            .map { }//여기부터는 위에서 메인스레드로 지정했으므로 메인스레드
            .map { }
            .map { }
            .subscribe(onNext: { [weak self] _ in
                self?.label.text = "안녕 반가워"
            })
            .disposed(by: disposeBag)
        
        button.rx.tap
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { [weak self] _ in
                self?.label.text = "안녕 반가워"
            })
            .disposed(by: disposeBag)

observe(on:) 메서드를 이용해 글로벌 쓰레드에서 동작하던 것을 메인 쓰레드에서 동작하게 바꿀 수 있음.

 

예를 들어, observe이전까지는 글로벌에서 동작하고 이후에 UI를 바꾸는 등의 코드를 작성할때는 observe를 이용해 메인스레드에서 진행되게함.

 

4. bind사용

        button.rx.tap
            .bind { [weak self] _ in
                self?.label.text = "안녕 반가워"
            }
            .disposed(by: disposeBag)

bind는 Subscribe와 유사하지만 MainSchedular의 동작과 error이벤트를 방출하지 않음.

-> 그래서 보통 UI쪽을 다룰 때는 bind를 사용하는듯

 

5. map 오퍼레이터를 이용해 데이터의 흐름을 조작할 수 있음.

        button
            .rx
            .tap
            .map { "안녕 반가워" }
            .bind(to: label.rx.text)
            .disposed(by: disposeBag)

tap의 ControlEvent<Void>를 map을 이용해 String타입으로 변경해주면, 간단하게 bind할 수 있음

 

이 경우엔 self도 쓰지 않아도 됨

 

 

6. driver traits 사용

//6 driver traits: bind + stream 공유(리소스 낭비 방지, share())
        button.rx.tap
            .map { "안녕 반가워" }
            .asDriver(onErrorJustReturn: "") //다른 타입으로 변경. 에러 발생할때 어떤 문자열을 쓸건지
            .drive(label.rx.text)
            .disposed(by: disposeBag)

이 경우엔 stream을 공유할 수 있어 리소스 낭비를 방지할 수 있다고함.

 

asDriver를 이용해서 driver타입으로 바꿔줌

 

driver는 메인 스레드에서의 실행을 보장함

 

Observable이 UI로 특화된 형태이므로 subscribe만 할 수 있고 값을 변경할 수는 없음.

 

drive는 내부적으로 share가 구현되어있음

 

정리하면, driver는 메인 스레드에서 사용되고 UI와 관련된 것에서 사용하면됨

-> Observable을 이용해서 observe를 이용해 스레드를 바꿔가며 사용해도되지만, 실수할 수도 있음

 

 

 

Share

 

일반적으로 Subscribe를 할 때마다 새로운 시퀀스가 생성됨

 

그래서 네트워크 요청같은 경우 share가 없다면 너무 많은 콜 수를 호출할 수도 있어 리소스가 낭비됨.

 

그래서 이러한 경우에에 share를 이용해 subscribe를 공유할 수 있게 처리함.

 

다시 말하지만 drive는 내부적으로 share가 구현되어있음.

'TIL' 카테고리의 다른 글

[TIL] 2022 / 11 / 1  (0) 2022.11.01
[TIL] 2022 / 10 / 27 - Rx  (0) 2022.10.27
[TIL] 2022 / 10 / 25 - Rx  (0) 2022.10.25
[TIL] 2022 / 10 / 24 - Rx  (0) 2022.10.24
[TIL] 2022 / 10 / 13 - Realm Migration  (1) 2022.10.13
댓글
최근에 올라온 글
Total
Today
Yesterday