이번주부터 Rx에 대해 모든 정리는 내가 이해한대로 말하듯이 쓸 것임.
틀린 부분이 있으면 댓글로 알려주시술...
Observer Pattern
Rx에서 가장 중요한 용어는 Observable과 Observer가 아닌가 싶다.
옵저버블은 이벤트를 전달하는 아이고 (방출하는), 옵저버는 이벤트를 전달받는 아이이다.
이게 말로는 쉬운데 개념을 화면 UI에 적용시키면 처음에 이해가 안가서..
내가 처음 이해한 방식으로 말하자면..
음,, 예를 들어서,
왈에서 발바닥 버튼을 눌러서 왈소리 라벨이 보이면
여기서 발바닥 버튼이 옵저버블이고
왈소리 라벨이 옵저버이다.
왜냐면 버튼의 이벤트로 왈소리가 보이게 됐기 때문이다.
버튼 눌렀으니까 라벨 내용 보여줘
수업 들으면서 헷갈린 부분이 이 아래 예시에서 어디가 옵저버블이고, 옵저버인지 모르겠는 거였는데
로그인 textField에서 사용자가 아이디를 입력할 때
textField의 글자가 5자 이상이 되면
button이 활성화되어서 색상이 바뀐다고 치면
글자가 5자 이상이니까 -> 색상 바꿔줘!
이벤트 전달했으니까 -> 이벤트 처리해줘!
그래서 textField가 옵저버블이고, button이 옵저버가 된다
여튼 간에
Observable은 이벤트를 보내고
Observer는 이벤트를 처리한다.
이놈들을 통해 데이터의 흐름을 통제하고, Operator를 통해 흐름을 변경하고 조작할 수 있다.
Observable과 Observer는 일대일인가??
유튜버는 영상을 올려서 알림을 보내면
구독자는 구독한 채널에 한해 알림을 받는다.
이 관계는 1:1이 아니다.
내가 구독하는 유튜버가 여럿일 수도 있고, 한 명일 수도 있다.
그래서 일대일, 일대다, 다대일이 가능하다.
Observable LifeCycle
시퀀스....
Sequence는 Rx에서 중요한 컨셉 중 하나다.
왜냐면 Rx에서 데이터는 위에서 아래로 흐르기 때문이고,
이거를 가능하게 하는 이유가 콜렉션 타입이 시퀀스 프로토콜을 채택하고 있기 때문이다.
이 데이터의 흐름을 따라가다보면 옵저버블은 총 3가지 이벤트를 전달한다.
1. Next Event (방출)
2. Error Event 에러 발생 - (Notification, 마지막에 전달 + 메모리 해제)
3. Complete Event 정상적 종료 - (Notification, 마지막에 전달 + 메모리 해제)
이거를 옵저버블의 라이프 사이클이라고 한다.
이렇게 종료된 이후에는 이벤트가 발생하지 않고, 옵저버블에 대한 재사용은 불가하다.
Infinite / Finite Observable Sequence
무한 이벤트(시퀀스) / 유한 이벤트(시퀀스)
검색 버튼을 3번 누르고 해당 버튼이 끝나버리는 경우는 없다.
UI가 무한 이벤트에 속하는 게 이와 같은 것이다.
그래서 무한 이벤트는 UI 관련 이벤트다.
반대로 유한 이벤트는 끝이 보이는 작업들, 예를 들어 사진을 다운로드 받거나 / 서버 통신과 같이
특정 작업을 수행 시에 완료 또는 성공 같은 결과가 있는 것들이 해당된다.
아래도 어쨌든 결과값을 반환하는 거라서 유한 이벤트에 해당
let result = array
.filter { $0 % 3 != 0 }
.map { $0 * 5 }
.reduce(0, +)
수업에서 이해할 때 보통
- 기능적인 요소가 Complete/Error Event라고 했고,
- UI적인 요소가 Next Event라고 했다.
그래서 예를 들어, 다마고치 물 먹이기 버튼을 누를 때마다 next 이벤트가 전달되는데
레벨 10이 되어서 다 자라면 제한을 주고 싶은 경우에는 subscribe을 취소시키면 된다.
이때 구독 취소 즉, dispose를 시켜 메모리에서 정리를 해주면 된다.
Q. 왜 구독 취소를 따로 해줘야 하냐?
A. 왜냐면 Next Event는 무한한 시퀀스라서 따로 dispose를 통해 메모리 해제가 처리되지 않는다.
끝이, 종료시점이 있는 이벤트가 아니기 때문이긔.
자,, 봐바라.. UI는 Next Event인데..
그러면 그 말은 즉, Complete/Error는 고려하지 않아도 된다는 것이잖아...?
그때 subscribe 대신 사용하는 아이가 바로 bind이다.
한마디로,
UI는 끝이 있는 놈이 아니라
next event만 방출하기에 다른 두 event는 고려하지 않아도 돼서
bind를 사용해 변경하자!
추후에 drive라는 아이도 나온다... 많기도 해라...
이 시점에 개발자인 내가 구독을 어떤 종류로 할 건지 고려해야 한다...
아 글구 일케 구독 안하면 아무리 이벤트 전달해봤자 의미없으니까
무조건 구독해야함
Disposable
얘는 프로토콜이다.
긍까,, 그 구독해제 즉, 시퀀스가 끝날 때 리소스 해제 처리를 해주는 놈이다.
사실 우리가 거의 신경 써줄 필요가 없는 게 클래스의 deinit 시점에 알아서 dispose가 동작한다.
= 옵저버블은 모두 Disposable을 반환한다는 뜻과 같다.
= 이를 통해서 시퀀스를 모두 종료한다.
= 옵저버블의 next 이벤트 방출이 끝나면 -> complete -> disposed로 정상 종료 및 해제된다.
뭐 특별한 경우만 빼고....
그렇다면 특별한 경우가 언제냐?!!
바로 무한한 시퀀스에 한해서 강제로 종료시켜서 해제시켜주는 경우에!!!
즉, next 이벤트가 무한히 방출될 수 있는 상황에서는 dispose가 안된다는 것이다.
또!!! rootVC에서는 메모리가 해제되지 않으니까 이럴 때는 별도 관리를 해줘야 한다.
근데 그 경우가 너무 많아서 하나하나 처리해주기가 너무 귀찮아 한 번에 처리해줄 때는 아래처럼
DisposeBag을 새롭게 할당하는 방식(인스턴스 초기화하는 방식)으로 한 번에 정리할 수 있다.
그러면 dispose 된 건지는 어케 알 수 있나요..?
onDispose 부분에서 출력이 되면,, dispose 된 것임!!
Q. 그냥 시퀀스의 마지막 disposed(by: DiposeBag())처럼 클래스 그 자체를 넣어주면 안되나요?
빌드하자마자 구독해제가 된다!
왜???
viewDidLoad 때 코드를 한 번 쫙 읽는데
DisposeBag() 클래스가 있으면 새로운 디스포즈백으로 갈아끼우는 형태가 돼서 바로 해제가 되어버린다.
withUnretained(self)
별개로 [weak self] 연산자를 대체하는 operator
Subject
https://www.notion.so/huree-can-do-it/10-25-DisposeBag-Subject-f9201515933a47deafb38807159de556
옵저버블은 구독이 안되는 애다.
긍까 전달만 할 수 있지 본인이 전달을 받을 수는 없다.
기존에 nickname에 "폴킴"이라는 옵저버블 값을
nicknameLabel이라는 옵저버에 전달하면 "폴킴"이라고 값이 바뀌겠지?
근데 이 nickname에게 다시 새롭게 값을 넣어줄 수가 없다...
그래서 나온 거가 Subject
Observable + Observer 자웅동체 가튼 녀석이다.
특징은 사실 옵저버블이랑 똑같다.
구독 전 이벤트는 무시하고, 후 이벤트만 처리한다.
종류는 여러가지다. PublishSubject, BehaviorSubject, ReplaySubject, AsyncSubject(잘안씀)
초기값이 없냐, 있냐, 몇 개 있냐에 따라서 잘 골라서 써주면 된다.
ReplaySubject에서 BufferSize는 메모리 과부하가 될 수 있으니까 주의하자!
Relay 등장!
RxSwift를 래핑해서 만든 게 RxCocoa인데,
Relay는 Subject가 RxCocoa로 래핑되면서 나온 개념이다.
거의 Subject랑 똑같은데 (다른 점은 Relay는 only Next만 존재~~~~)
PublishRelay, BehaviorRelay, ReplayRelay~~
키워드만 onNext -> accept로 변경
왜 등장하게 되었냐????
데이터를 UI에 보여주는 정도면 onNext만 쓰지,,,,,,,, error, complete는 거의 쓰지 않음
즉, ui를 좀 더 잘 쓰기 위해서!
Bind? Subscribe? Driver?
이벤트를 구독할 때 bind 또는 subscribe를 사용하는데
Observable은 subscribe한다.
- 백그라운드에서 동작할 수 있는 가능성이 있어서 메인스케줄러에서 동작할 수 있도록 변경해주는 부분이 어느정도 필요하다.
UI와 관련된 요소는 bind를 사용하고,
- 메인 스레드에서만 작동하기 때문에 메인스케줄러 관련 코드를 작성하지 않아도 된다.
- onError 처리도 할 필요가 없지~?
Driver는 bind랑 비슷한데
- stream이 공유될 수 있어서 리소스 낭비 방지할 수 있다.
- (내일 내용 추가할 것임)
on(_ event:) vs onNext(_ )
이벤트를 꺼내서 쓰는 경우에는 후자를 더 많이 쓴다.
on은 next, error, complete도 되기 때문에 한 번 더 래핑을 해줘야 하기 때문
10/26 TIL https://huree-can-do-it.notion.site/10-26-Relay-e9087f15916b42768178889cd4ea8b1a
10/25 TIL https://huree-can-do-it.notion.site/10-25-DisposeBag-Subject-f9201515933a47deafb38807159de556
10/24 TIL https://huree-can-do-it.notion.site/10-24-RxCocoa-Rx-74f45ff4543842cb8be2ca47924d7371
'⭐️ 개발 > Rx' 카테고리의 다른 글
7. Traits - ControlProperty, ControlEvent, Driver (0) | 2023.01.16 |
---|---|
6. Binder (0) | 2023.01.16 |
[Rx] Error Operator (0) | 2023.01.16 |
[Rx] Input/Output 패턴 적용하기 - 비즈니스 로직 분리!! (4) | 2022.11.02 |
[Rx] TIL - Rx 개념 재복습 및 총정리 (0) | 2022.10.31 |