RxSwift로 개발을 하다보면,
특정 옵저버블이 옵저버에게 전달해주는 이벤트를 처리하는 작업을
특정쓰레드에서 진행되도록 해야하는 경우가 있습니다.
UI관련 로직이라면 반드시 메인쓰레드에서 해당 작업이 이뤄나도록 해야하는 것 처럼요.
Rx는 Scheduler라는 것을 사용하여 멀티 쓰레드 환경을 관리합니다.
먼저 Rx에서 Scheduler가 의미하는 것이 어떤 것인지 살펴봅시다.
Scheduler
MainScheduler,
ConcurrentDispatchQueueScheduler,
SerialDispatchQueueScheduler,
OperationQueueScheduler,
ImmediateScheduler,
CurrentThreadScheduler
우선 Rx에는 다양한 스케쥴러가 존재하는데요, qos, 동시성, 직렬과 같은 말이 많은 것으로 보아
GCD와 유사하게 동작한다 정도만 알고 가면 좋습니다.
Rx가 스케줄러를 사용하는 이유는 Subscription코드와 Observation코드가 실행될 공간을 지정해주기 위해서입니다.
Subscription code & Observation code
여기에 옵저버블이 있습니다.
옵저버블 생성시 전달되는 클로저를 Supscription code라고 합니다.
해당 옵저버블을 구독해보겠습니다.
옵저버 생성시 전달되는 클로저를 Observation code라고 합니다.
옵저버블과 옵저버에 있는 코드들은 어떤 쓰레드에서 언제실행될까요?
정답을 말씀드리면, 우선 기본적으로 subscribe가 호출된 쓰레드에서 해당 클로저들이 호출되게 됩니다.
여기서 중요한 점은 subscribe가 호출된 쓰레드가 옵저버블의 클로저가 호출되는 쓰레드를 결정한다는 점입니다.
어떻게 이게 가능할까요?
왜냐하면, 옵저버블은 전달받은 클로저를 구독이 발생한 시점이 실행시키기 때문입니다.
내부적으로 코드를 보면
create함수는 내부적으로 AnonymouseObservable인스턴스를 생성하고 반환합니다.
해당 인스턴스는 생성시전달한 클로저를 생성자에 전달받습니다.
그러면 실질적으로 subscribe(onNext:)함수는
AnonymouseObservable인스턴스의 subscibe(onNext:)매서드를 호출하게 됩니다.
subscribe(onNext:)를 함수 내부를 살펴보면
옵저버블 안에서 AnonymousObserver를 생성하는 것을 확인할 수 있습니다.
옵저버는 생성시 클로저를 전달받는데, 해당 클로저 내부에 onNext클로저가 호출되는 것을 알 수 있습니다.
옵저버를 생성한 이후 subscribe(onNext:)매서드는 Disposables를 반환하며 종료되는데,
옵저버블(AnonymousObservable)에 생성한 옵저버를 구독시키며, 생성된 Disposable을 만들며 종료됩니다.
현재까지 진행과정을 정리하면 아래와 같습니다.
옵저버는 옵저버블(=Producer)의 subscribe(observer)를 호출하는데요 (onNext아님 주의)
이함수에서 옵저버블을 생성할 때 전달한 클로저가 실행되고 매개변수로는 옵저버가 전달되게 됩니다.
바로 create 호출시 전달한 그 클로져(Subscription code)요!
살짝 비약이 있는데요, 우선 AnonymousObservable은 구독발생시 AnonymousObservableSink라는 객체를 만들고,
해당 객체의 run매서드를 호출합니다.
구독함수가 호출된 쓰레드에서 해당 함수를 호출하기 때문에,
옵저버블 생성시 호출한 클로저(Subscription code)는 결과적으로 구독이 발생한 쓰레드에서 호출되게되는 겁니다~!
그럼 구독시 전달한 클로저(Observation code)는 언제 호출되는 것일까요?
해당 부분을 확인하려면 Subscription code를 살펴보면 됩니다.
해당 클로저의 매개변수로 옵저버가 전달되게 되고 이벤트가 옵저버에
onNext코드를 실행하면, AnonymousObservableSink라는 타입으로 이벤트를 전달하게 됩니다.
Sink?
~Sink라는 객체들은 구독이 발생할 때 생성되게 됩니다.
앞선 사진에 누락된부분으로 아래사진처럼 AnonymousObservableSink객체를 구독시 생성합니다.
~Sink 객체들은 Sink클래스를 상속받는데요, Sink클래스가 가진 프로퍼티와 매서드중
주목할 것은 아래 박스친 observer와 옵저버에 이벤트를 전달하는 forawrdOn이라는 매서드입니다.
Sink는 객체는 옵저버블의 subscribe(observer)매서드가 호출될 때 생성되는데요,
해당 매서드로 전달받은 옵저버가 Sink객체 생성시 그대로 전달되게 됩니다.
잠깐 정리해보겠습니다.
1. 구동발생시 옵저버가 생성된다.
2. 이후 subscribe(observer)매서드가 호출되며 매개변수로 생성된 옵저버가 전달된다.
3. subscribe(observer)함수는 Sink객체를 만들며 Sink객체의 observer프로퍼티는 생성된 옵저버를 참조한다.
4. 옵저버는 Sink객체의 forwardOn매서드에 의해서 이벤트를 전달받게된다.
사실상 옵저버는 Sink객체로 랩핑된다고 이해하시면 됩니다!!!
forawdOn의 경우 Subscription code에서 옵저버(Sink객체, 최초 옵저버를 랩핑)에게 on(event)매서드로 이벤트를 전달하게되고
해당 매서드에서 forwardOn을 실행하게 됩니다.
그림으로 나타내면 다음과 같습니다.
지금까지의 흐름을 보면 크게 구독에 의해 이벤트를 방출하는 과정과
방출된 이벤트가 옵저버에게 도달하기 까지의 과정으로 나눌 수 있다는 것을 알 수 있습니다.
Rx오퍼레이터가 동작하는 방식
해당 오퍼레이터들을 활요하기 위해 Sink함수에 대해 조금더 설명해보겠습니다.
map오퍼레이터의 경우는 어떨까요?
코드를 살펴보면 옵저버블과 오퍼레이터가 동작하는 방식과 같습니다.
AnonymousObservable처럼 Map객체 생성됩니다.
그리고 해당 객체에 구독이 발생하는 경우 Sink객체가 생성되게 됩니다.
그림으로 보면 다음과 같습니다.
우리가 일반적으로 map오퍼레이터에 전달하는 클로저는 transform이라는 프로퍼티에저장되고, forwardOn함수전에 호출됩니다.
즉, 우리가 일반적으로 전달하는 클로저는 구독시에 호출된다는 것을 알 수 있습니다.
자 서론이 길었습니다.
이제 subscribe(on:), observe(on:)의 역할을 설명할 수 있습니다.
짐작하시겠지만, subscribe(on:)은 subscription code(클로저)가 실행될 스케쥴러를 지정합니다.
observe(on:)은 observation code(클로저)가 실행될 스케쥴러를 지정합니다.
정확하게는 subscribe(on:)의 경우 Sink객체를 생성하고 옵저버블의 supscribtion code를 실행하는 스케쥴러를 지정합니다.
내부적으로 살펴보면 SubscriveSink객체가 특정 스케줄러 내부에서 구독 함수를 실행함을 알 수 있습니다.
즉 그림에서 노란색 영역의 작업들이 실행될 스케줄러를 지정할 수 있습니다!
observe(on:)의 경우 옵저버가 이벤트를 수신하는 작업이 진행되는 스케쥴러를 지정합니다.
아래로 진행되는 모든 작업이 해당스케줄러를 따르게 됨을 알 수 있습니다.
그 이유는 이후에 있을 모든 코드 호출이 스케쥴러에서 실행된 코드에서 파생되기 때문입니다.
만약 해당 오퍼레이터가 다수 사용된다면 아래 그림처럼 영향이 미칩니다.
observe(on:)의 경우 내부구현이 subscribe보다는 코드가 복잡했습니다.
간략하게 설명하자면, 내부적으로 NSLock을 사용하고, 방출된 이벤트를 queue에 등록하고 큐에서 작업을 꺼내오는 방식으로 이벤트가 진행됨을 알 수 있습니다.
마블 다이어그램
자 여기까지 살펴보면 마블 다이어그램이 조금 이해가 됩니다.
처임 봤을 때는 무슨 말인지(설명이 너무 간략하기도 하고,,,) 잘 몰랐는데 이제 느낌이 조금 오는 것 같습니다.
역시 개발자는 코드를 봐야하나봐요..
외 윗부분이 파란색인지! observeOn아래는 왜 색이 변했는지! 한번 음미해보시길 바랍니다.
감사합니다!!!
'iOS공통' 카테고리의 다른 글
[Swift] 이미지 캐싱을 통한 로딩속도 최적화 (0) | 2024.09.23 |
---|---|
[아키텍처 패턴] MVP에서 VIPER까지 (0) | 2024.08.28 |
[자료구조] 해시 테이블(+Swift) (0) | 2024.08.16 |
[Swift] GCD와 Swift concurrency에 대해 (0) | 2024.07.22 |
[RxSwift] self순환 참조에 대해 (0) | 2024.07.19 |