iOS/RxSwift

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

소들이 2023. 5. 10. 18:33

 

 

안녕하세요! 소들입니다

저번 포스팅에서 Observable을 생성하는 간단한 Operator를 몇 개 알아봤잖아요?

(just, of, from, create)

이번에도 마찬가지입니다! 어렵지 않으니 이번 내용은 얼른 하고 넘어가죠

모든 포스팅은 편의 말투로 합니다~!!

 

 

 

 

1. Observable을 생성하는 또다른 방법

 

공부하기 전에

기억해야 하는 사실 하나만 짚고 넘어가자면,

 

onCompleted이나 onError가 호출되고 난 후에 그 Observable은 

dispose 되어버리기 때문에 더이상 어떠한 이벤트도 방출할 수 없습니다

 

 

 

 

1-1. empty

 

어떠한 항목(Item)도 방출(emit)하지 않고,

즉시 onCompleted()을 호출하여 정상적으로 종료되는 Observable을 생성합니다

 

 

말 그대로 비었거등요 ...

그래서 어떠한 이벤트 항목을 방출시키지 않음!!!

다만 Empty Operator를 통해 Observable을 생성할 경우,

onCompleted() 메서드를 호출 후 스트림이 "정상 종료" 됨!!

 

따라서, 어떠한 항목 방출 없이 바로 정상적으로 스트림을 종료하고 싶을 때 사용됨!

 

 

let observable = Observable<Void>.empty()
 
 
observable.subscribe(onNext: { (data) in
    print(data)
},
onCompleted: {
    print("onComplete")
},
onDisposed: {
    print("onDispose")
})
.disposed(by: disposeBag)

 

 

실제 코드로 테스르를 해보면, 

 

 

 

 

어떤 항목도 방출되지 않기 때문에, onNext의 이벤트는 불리지 않고,

바로 onComplete가 불리며 스트림이 정상 종료됨

 

 

 

1-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: disposeBag)

 

 

위 코드를 실행시켜 보면,

아무런 결과값이 찍히지 않음

 

앞서 말했듯,

이벤트를 방출하지도, completed을 통해 정상종료를 하지도 않기 때문

그럼 얘 죽었냐 ...? 싶지만..

아니..! 종료되지 않는다 했잖음!! 계속 살아있음!!!

그럼 ㅁㅔ모리 해제 언제 됨 ..?

앞서 말했듯 해당 Observable이 dispose될 땐

(위 코드에선 disposeBag이 메모리에서 해제될 때)

그땐 onDisposed 메서드가 실행되며 스트림이 종료됨

 

 

 

 

쨔란

 

 

 

1-3. range

 

range는 특정 범위의 "정수"를 순서대로 발행하는 Observable을 생성합니다

 

 

 

 

중요한 것은, 해당 범위 안의 "정수"를 모두 발행한 경우,

onCompleted()이 불리며 스트림이 종료됨!!!

움.. 어렵지 않으니 예제로 보고 넘어갈게영

 

 

let observable = Observable.range(start: 0, count: 5)

 

 

위 코드의 경우, 0부터 5번만큼 정수를 방출하고 

 

 

 

 

onComplete이 불리며 dispose 되는 것을 볼 수 있음!!

당연히 "정수"를 발행하는 Observable 생성자이기 때문에,

 

 

 

 

Observable이 방출하는 타입은 Int로 지정되어 있어서

건들면 아..앙대..

 

 

 

1-4. interval

 

interval 연산자는 주어진 시간 간격으로 순서대로 정수를 발행하는 Observable을 생성 합니다

구독을 해제하기 전까지 이벤트를 무한히 방출하기 때문에, 

불필요해졌을 경우 dispose를 해주어야 합니다

 

 

 

 

 

위 Range와 다른 점은

Range의 경우 주어진 범위 만큼 방출하고 onCompleted()이 불렸다면,

interval은 내가 불필요해져서 dispose를 해주기 전까진

주어진 시간 간격마다 무한하게 이벤트를 방출함!!

 

따라서 꼭 주의할 점!

"불필요 할시 dispose를 해주어야 합니다"

(물론 위 코드에선 disposeBag에 담았기 때문에,

disposeBag이 처분될 때 같이 처분될 것임)

 

예제는 코드로 보쟈

 

 

let observable = Observable<Int>.interval(.seconds(1),
                                          scheduler: MainScheduler.instance)

 

 

위와 같이 지정한단 것은

1초마다 정수를 발행하는 Observable을 생성한단 것이고,

dispose를 행하기 전까진 정수가 무한하게 발행되고 있을 것임

 

 

 

 

죽이기 전까지 무한하다능

 

 

 

1-5. timer

 

구독 시점으로부터 특정 시간 동안 이벤트 방출을 지연시킨 뒤,

지연된 후 이벤트를 방출하고 나서 onCompleted()이 호출되며 정상 종료됩니다

 

 

얜 그리기 귀찮아서 공식문서꺼 훔쳐옴 ㅌㅌ

 

 

살짝 헷갈릴 수 있으나

"구독한 시점"으로부터 특정 시간을 지연시켜 이벤트를 방출한다고 생각하면 편할 것 같음

쨌든 우리가 원래 쓰던 타이머같이 지연 후에 이벤트를 방출하고,

onCompleted()을 통해 자동으로 정상 종료(dispose)가 됨

 

 

let observable = Observable<Int>.timer(.seconds(3),
                                      scheduler: MainScheduler.instance)

 

 

위처럼 선언해줄 수 있고,

 

 

 

 

이 경우, 실제 테스트 해보면

구독을 한 후 3초 뒤에 이벤트를 방출하는 것을 볼 수 있음

또 방출이 끝난 후에 onComplete()이 불리며 dispose 됨!!

 

여기서 조심하셔야 할 건, 

사실 이 timer 메서드는 두번 째 파라미터를 갖고 있었다는 것.. !!!!!! (두둥)

 

 

let observable = Observable<Int>.timer(.seconds(1),
                                      period: .seconds(2)
                                      scheduler: MainScheduler.instance)

 

 

이렇게 period란 파라미터를 설정해줄 수 있는데 (기본값은 nil)

period라는 것은 말 그대로 다음 값이 생성되기까지의 주기를 지정하는 것임!!

 

위처럼 내가 period를 2초라고 줘버리면,

위 Observable은 기존처럼 1초뒤에 이벤트를 방출하고 종료되는 것이 아니라

1초 뒤에 이벤트를 방출 - 2초 쉬고 - 이벤트 방출 - 2초 쉬고 - 이벤트 방출 ... 무한 반복

..ㅎㅎ 이 경우 Interval과 같이 무한으로 방출됨

때문에 period를 사용한 경우, disposeBag을 사용하든 직접 dispose를 시켜줘야함!!!

 

 

 

1-6. defer

 

Observer가 구독할 때까지 Observable의 생성을 지연시켜줍니다.

Subscribe() 메서드를 호출 할 때 Observable을 생성합니다

 

 

여러분은 지금 그림그리다 지쳐 공식문서 그림을 훔쳐오는 소들을 보고 계십니다

 

 

이게 먼소리잉겨,, 싶잖음 ,,??

여러분 우리가 지금까지 onNext를 직접 원하는 때에 호출하던 Operator는 제외하고

just, of 등등.. 으로 만든 Observable은

내가 해당 Observable을 생성하는 코드가 실행되는 시점에 Observable이 바로 생성 됨

그리고 이제 그 Observable에 대고 "구독을 하면",

그 이미 생성 시 발생된 이벤트가 순서대로 방출되는 것임

 

이건 예제로 보는 게 더 쉬울 것 같음

 

 

let observableJust = Observable<String>.just(getTime() + " ➕ Just")
 

 

 

현재 시간을 찍는 메서드 getTime이 있을 때,

위처럼 getTime을 통해 얻은 시간을 방출하는 Observable을 생성했음

그리고

 

 

DispatchQueue.main.asyncAfter(deadline: .now() + 60, execute: {
    observableJust.subscribe(onNext: { (data) in
        print("현재시간 \(getTime()), Observable 생성 시간 \(data)")
    })
    .disposed(by: self.disposeBag!)
})
 

 

 

Observable이 생긴 코드가 실행된 후 바로 그 Observable을 구독하는 게 아니라,

60초가 지난 후에 해당 observable을 구독하게 했음!!

그리고 나서 현재 시간(구독 시간)과 Observable이 방출한 시간을 찍어보면

 

 

 

 

이처럼 차이가 나는 것을 볼 수 있음

무슨 말이냐면, 구독을 하는 순간 Observable을 생성하는 게 아니라,

이미 Observable은 생성 코드가 불릴 때 생성이 되고,

구독을 하는 시점에, 그때 생성된 Observable의 항목이 방출 되는 것임!!!

 

그렇다면 defer는 어떨까?

 

 

 

 

defer 연산자는 deferred라는 메서드를 통해 만드는데,

이 defferd의 파라미터는 escaping 클로저로,

반환 타입이 Observable<Elemen>임..!

즉, 우리가 직접 클로저로 어떤 Observable을 생성할 건지를 전해줘야 함

 

 

let observableDefer = Observable<String>.deferred {
    return Observable.just(getTime() + " ➕ defferd")
}
 

 

 

따라서 위처럼 작성할 수 있는데,

이제 다시 60초가 지난 후에 해당 observable을 구독하게 하면

 

 

DispatchQueue.main.asyncAfter(deadline: .now() + 60, execute: {
    observableDefer.subscribe(onNext: { (data) in
        print("현재시간 \(getTime()), Observable 생성 시간 \(data)")
    })
    .disposed(by: self.disposeBag!)
})
 

 

 

이번엔 말임

 

 

 

 

Observable이 생성된 시간과 현재 시간(구독 시간)이 동일한 것을 볼 수 있음!!

아하! 정리하자면 defer Operator는,

 

구독 전에는 Observable이 생성되지 않다가,

Observer가 구독을 시작하면, 이때 Observer 별로 새로운 Observable을 생성해서 구독하고 있구나!

 

정도가 되겠음

따라서 deffer 연산자의 경우,

 

구독 직전에 Observable을 생성하여 가장 최신 상태의 아이템을 방출할 때 사용 함

 

-끗-!

 

 

 

 

 

 

 

연산자가 쉬워서 금방 끝날 줄 알아쓴데..

포스팅ㅇ이 재미가 엄서.. 킁

오타 잘못된 내용, 피드백 대환영입니다!!!!