Search
Duplicate

RxSwift - Observable 생성하기(empty, never, range, interval, timer, defer)

Created
2023/09/17 02:36
Tags
RxSwift

RxSwift - Observable 생성하기(empty, never, range, interval, timer, defer)

 Observable을 생성하는 또다른 방법

 주의!) onCompleted나 onError가 호출되고 난 후에 그 Observable은 dispose 되어버리기 때문에 더이상 어떠한 이벤트도 방출할 수 없다.

 1. empty

어떠한 항목(item)도 방출(emit)하지 않고, 즉시 onCompleted()을 호출하여 정상적으로 종료되는 Observable을 생성한다.
말 그대로 비어있다.
그래서 어떠한 이벤트 항목을 방출시키지 않는다.
다만, Empty Operator를 통해 Observable을 생성할 경우,
onCompleted() 메서드를 호출 후 스트림이 ‘정상 종료’ 된다.
따라서, 어떠한 항복 방출 없이 바로 정상적으로 스트림을 종료하고 싶을 때 사용된다.
let obserable = Observable<Void>.empty() observable.subscribe(onNext: { (data) in print(data) }, onCompleted: { print("onComplete") }, onDisposed: { print("onDispose") }) .disposed(by: disposeBag)
Swift
복사
onComplete onDispose
Shell
복사
어떤 항목도 방출되지 않기 때문에, onNext의 이벤트는 불리지 않고, 바로 onComplete가 불리며 스트림이 정상 종료된다.

 2. never

어떠한 항목(item)도 방출(emit)하지 않고, 스트림이 종료되지도 않는 Observable을 생성한다.
어떠한 이벤트 항목을 방출시키지 않는다는 점에서 empty와 동일하나, empty는 즉시 onCompleted()을 호출시켜 스트림을 정상 종료하는 방면, never는 onCompleted()도 호출시키지 않아, 스트림이 종료되지도 않음
따라서 직접 dispose를 실행시켜 주거나
disposeBag을 이용해 dispose되기 전까진 절대 스트림이 종료되지 않는다.
무한정 지속성을 나타낼 때 사용한다고 한다.
let observable = Observable<Void>.never() observable.subscribe(onNext: { (data) in print(data) }, onCompleted: { print("onComplete") }, onDisposed: { print("onDispose") }) .disposed(by: disopseBag)
Swift
복사
아무런 결과값이 출력되지 않는다.
앞서 말했듯이, 이벤트를 방출하지도, completed을 통해 정상종료도 하지도 않기 때문이다.
메모리 해제는 해당 Observable이 dispose될 때 onDispoed 메서드가 실행되며 스트림이 종료된다.
onDispose
Shell
복사

 3. range

range는 특정 범위의 ‘정수’를 순서대로 발행하는 Observable을 생성한다.
중요한 것은, 해당 범위 안의 ‘정수’를 모두 발행한 경우, onCompleted()이 불리며 스트림이 종료된다.
let observable = Observable.range(start: 0, count: 5)
Swift
복사
0부터 5번만큼 정수를 방출하고
0 1 2 3 4 onComplete onDispose
Shell
복사
onComplete이 불리며 dispose되는 것을 볼 수 있다.
당연히 ‘정수’를 발행하는 Observable 생성자이기 때문에

 4. interval

interval 연산자는 주어진 시간 간격으로 순서대로 정수를 발행하는 Observable을 생성한다.
구독을 해제하기 전까지 이벤트를 무한히 방출하기 때문에, 불필요해졌을 경우 dispose를 해주어야 한다.
Range와 다른 점은 Range의 경우, 주어진 범위 만큼 방출하고 onCompleted()이 불렸다면, interval은 내가 불필요해져서 dispose를 해주기 전까진 주어진 시간 간격마다 무한하게 이벤트를 방출한다.
따라서, 불필요할시, dispose를 해주어한다.
let observable = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instanc)
Swift
복사
위와 같이 지정한다는 것은, 1초마다 정수를 발행하는 Observable을 생성하다는 것이고,
dispose를 행하기 전까진 정수가 무한하게 발행되고 있을 것이다.
0 1 2 3 4 5 6 7 8 9
Shell
복사
dispose 하기전까진 계속

 5. timer

구독 시점으로부터 특정 시간 동안 이벤트 방출을 지연시킨 뒤,
지연된 후 이벤트를 방출하고 나서 onCompleted()이 호출되며 정상 종료된다.
‘구독한 시점’으로부터 특정 시간을 지연시켜 이벤트를 방출한다고 생각하면 된다.
타이머같이 지연 후에 이벤트를 방출하고, onCompleted()을 통해 자동으로 정상 종료(dispose)가 된다.
let observable = Observable<Int>.timer(.seconds(3), scheduler: MainScheduler.instance)
Swift
복사
o onComplete onDispose
Shell
복사
실제 테스트를 해보면, 구독을 한 후 3초 뒤에 이벤트를 방출하는 것을 볼 수 있다.
또 방출이 끝난 후에 onComplete()이 불리며 dispose가 된다.
timer 메서드는 두 번째 파라미터를 가지고 있다.
let observable = Observable<Int>.timer(.seconds(1), period: .seconds(2), scheduler: MainScheduler.instance)
Swift
복사
period 라는 것은 말 그대로 다음 값이 생성되기까지의 주기를 지정하는 것이다.
period를 2초라고 주면, Observable은 기존처럼 1초 뒤에 이벤트를 방출하고 종료되는 것이 아니라,
1초뒤에 이벤트를 방출 → 2초 쉬고 → 이벤트 방출 → 2초 쉬고 → 이벤트 방출 (무한 반복)
→ Interval 같이 무한으로 방출된다.
때문에 period를 사용한 경우, disposeBag을 사용하든 직접 dispose를 시켜줘야한다.

 6. defer

Observer가 구독할 때까지 Observable의 생성을 지연 시켜준다.
Subscribe() 메서드를 호출할 때 Observable을 생성한다.
이때가지는 해당 Observable을 생성하는 코드가 실행되는 시점에 Observable이 바로 생성되었다.
그리고 이제 그 Observable에 대고 ‘구독을 하면’, 이미 생성 시 발생된 이벤트가 순서대로 방출되었었다.
let observableJust = Observable<String>.just(getTime() + " + Just")
Swift
복사
현재 시간을 출력해주는 메서드 getTime이 있을 때,
getTime을 통해 얻은 시간을 방출하는 Observable을 생성했다.
그리고
DispatchQueue.main.asyncAfter(deadline: .now() + 60, execute: { observableJust.subscribe(onNext: { (data) in print("현재시간 \(getTime()), Observable 생성 시간 \(data)") }) .disposed(by: self.disposeBag!) })
Swift
복사
Observable이 생긴 코드가 실행된 후 바로 그 Observable을 구독하는것이 아니라,
60초가 지난 후에 해당 obervable을 구독하게 하였다.
그리고 나서 현재시간(구독시간)과 Observable이 방출한 시간을 출력해보면
이렇게 차이가 나는 것을 확인할 수 있다.
구독을 하는 순간 Observable을 생성하는 것이 아니라, 이미 Observable은 생성 코드가 불릴 때 생성이 되고, 구독을 하는 시점에, 그때 생성된 Observable의 항목이 방출되는 것이다.
그러면 defer 는?
public static fund deferred(_ observableFactory: @secaping () -> throws -> Observable<Element>) -> Observable<Element> { Deferred(observableFactory: observableFactory) }
Swift
복사
defer 연산자는 deferred 라는 메서드를 통해 만드는데,
이 defferd의 파라미터는 escaping 클로저로,
반환 타입이 Observable<Element> 이다.
즉, 우리가 직접 클로저로 어떤 Observable을 생성할 건지를 전해주어야 한다.
let observableDefer = Observable<String>.deferred { return Observable.just(getTime() + " + deferred") }
Swift
복사
이렇게 다시 60초가 지난 후에 해당 observable을 구독하게 하면
DispatchQueue.main.asyncAfter(deadline: .now() + 60, execute: { observableDefer.subscribe(onNext: { (data) in print("현재시간 \(getTime()), Observable 생성 시간 \(data)") }) .disposed(by: self.disposeBag!) })
Swift
복사
이번에는 Observable이 생성된 시간과 현재 시간(구독 시간)이 동일한 것을 볼 수 있다.
defer Operator는, 구독 전에는 Observable이 생성되지 않다가, Observer가 구독을 시작하면, 이때 Observer 별로 새로운 Observable을 생성해서 구독하고 있는 것이다.
따라서 defer 연산자의 경우,
구독 직전에 Observable을 생성하여 가장 최신 상태의 아이템을 방출할 때 주로 사용한다.

 Reference