take Operator에 대하여(RxSwift)

4 분 소요

flatMap에서 take를 써야하는 경우

단도직입적으로 말하자면 아래와 같은 경우에는 반드시 써야한다.

_ = button.rx.tap.debug("TAP")
            .flatMap {
                return BehaviorRelay(value: Int.random(in: 0...100)).debug("내부 Observable")
            }.debug("flatMap 이후")
            .subscribe()

내부 Observable에 BehaviorRelay를 써줬지만 저 부분에는 다른 Observable들이 충분히 들어올 수 있다. 이를테면 끝나는 시점이 명확히 정해지지 않은 Observable들 (특히 DelegateProxy 관련)

실행 후 버튼을 세번 눌렀을 때의 결과

flatMap 이후 -> subscribed
TAP -> subscribed
//버튼 탭 
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(42)
flatMap 이후 -> Event next(42)
//버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(11)
flatMap 이후 -> Event next(11)
//버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(81)
flatMap 이후 -> Event next(81)

내부 Observable을 subscribe하는 부분만 존재하고 dispose하는 부분이 존재하지 않는다는 것을 알 수 있다. 모식도를 그려보면 아래와 같다.

TAP              --T------------T------------T--...-->
내부 Observerable --42--...-->  -11--...-->  -81--...-->
flatMap 이후       -42-----------11-----------81--...-->

내부 Observable에 대한 시퀀스가 버튼을 탭할때마다 만들어지고 subscribe되며 dispose되지 않는다. 따라서 flatMap 내부에서 반환되는 Observable이 ‘끝나는 시점이 명확하지 않은 Observable’이라고 한다면 take와 같은 제한 사항을 두도록 하자

_ = button.rx.tap.debug("TAP")
            .flatMap {
                return BehaviorRelay(value: Int.random(in: 0...100)).debug("내부 Observable").take(1)
            }.debug("flatMap 이후")
            .subscribe()

위와 같이 말이다.

어? 그러면 바깥쪽에 take를 한다면 어떻게 되나요? -> 아래에서 살펴보자

 

내부 Observable에 take를 안썼다면 언제 disposed될까?

그렇다면 내부 Observable 시퀀스들은 언제까지 살아있게 될까? 영원히? 그렇지는 않다. 아래의 코드를 살펴보자.

@IBOutlet weak var button: UIButton!
@IBOutlet weak var secondButton: UIButton!

let disposable = button.rx.tap.debug("TAP")
            .flatMap {
                return BehaviorRelay(value: Int.random(in: 0...100)).debug("내부 Observable")
            }.debug("flatMap 이후")
            .subscribe()
        
_ = secondButton.rx.tap.debug("두번째 버튼")
            .subscribe(onNext: {
                disposable.dispose()
            })
		

버튼을 두개 만들었고, 두번째 버튼을 누른 경우 첫번째 버튼에 대한 Observable 시퀀스가 dispose되도록 만들었다. 첫번째 버튼을 세번 탭한 뒤 두번째 버튼을 한번 탭한 결과는 아래와 같다.

flatMap 이후 -> subscribed
TAP -> subscribed
두번째 버튼 -> subscribed
//첫번째 버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(16)
flatMap 이후 -> Event next(16)
//첫번째 버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(36)
flatMap 이후 -> Event next(36)
//첫번째 버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(16)
flatMap 이후 -> Event next(16)
//두번째 버튼 탭
두번째 버튼 -> Event next(())
flatMap 이후 -> isDisposed
TAP -> isDisposed
내부 Observable -> isDisposed
내부 Observable -> isDisposed
내부 Observable -> isDisposed

기존 Observable이 dispose되면 내부에서 살아있던 Observable들도 모두 dispose되는 것을 알 수 있다.  

외부에다가 take를 쓴다면?

_ = button.rx.tap.debug("TAP")
            .flatMap {
                return BehaviorRelay(value: Int.random(in: 0...100)).debug("내부 Observable")
            }.debug("flatMap 이후")
            .take(1)
            .subscribe()

위와같이 작성한 경우 결과는 아래와 같다.

flatMap 이후 -> subscribed
TAP -> subscribed
//버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(7)
flatMap 이후 -> Event next(7)
flatMap 이후 -> isDisposed
TAP -> isDisposed
내부 Observable -> isDisposed

외부 Observable자체가 dispose된 후 마지막으로 내부 Observable이 종료되었다.  

내부 Observable들이 살아있을 때 해당 내부 Observable들에 데이터가 생긴다면 이게 작동에 영향을 미칠까?

let testSubject: BehaviorSubject<Int> = BehaviorSubject(value: 0)
        
_ = button.rx.tap.debug("TAP")
            .flatMap {
                return testSubject.debug("내부 Observable")
            }.debug("flatMap 이후")
            .subscribe()
        
_ = secondButton.rx.tap
            .subscribe(onNext: {
                testSubject.onNext(Int.random(in: 1...100))
            })

첫번째 버튼을 한번 눌러 내부 Observable이 subscribed되도록 한 뒤 두번째 버튼을 여러번 눌러보자.

flatMap 이후 -> subscribed
TAP -> subscribed
//첫번째 버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(0)
flatMap 이후 -> Event next(0)
//두번째 버튼 탭
내부 Observable -> Event next(55)
flatMap 이후 -> Event next(55)
//두번째 버튼 탭
내부 Observable -> Event next(13)
flatMap 이후 -> Event next(13)
//두번째 버튼 탭
내부 Observable -> Event next(62)
flatMap 이후 -> Event next(62)

내부 Observable이 살아있는 경우 첫번째 버튼을 탭하지 않았음에도 두번째 버튼 탭에 따라 동작이 되는 것을 확인할 수 있다. 이는 원치 않는 동작을 일으킬 가능성이 있다.

 

take의 위치에 따라 결과가 달라질까?

take가 위쪽에 위치한 경우

_ = button.rx.tap.debug("TAP")
            .take(1)
            .flatMap {
                return Observable.from([1, 2]).debug("내부 Observable")
            }.debug("flatMap 이후")
            .subscribe()
flatMap 이후 -> subscribed
TAP -> subscribed
//버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(1)
flatMap 이후 -> Event next(1)
내부 Observable -> Event next(2)
flatMap 이후 -> Event next(2)
내부 Observable -> Event completed
내부 Observable -> isDisposed
TAP -> isDisposed  //take에 의해 영향을 받은 부분
flatMap 이후 -> Event completed
flatMap 이후 -> isDisposed

take가 아래쪽에 위치한 경우

_ = button.rx.tap.debug("TAP")
            .flatMap {
                return Observable.from([1, 2]).debug("내부 Observable")
            }.debug("flatMap 이후")
            .take(1)
            .subscribe()
flatMap 이후 -> subscribed
TAP -> subscribed
//버튼 탭
TAP -> Event next(())
내부 Observable -> subscribed
내부 Observable -> Event next(1)
flatMap 이후 -> Event next(1)
flatMap 이후 -> isDisposed  //take에 의해 영향을 받은 부분
TAP -> isDisposed
내부 Observable -> Event next(2)
내부 Observable -> Event completed
내부 Observable -> isDisposed

take로 인해 외부 Observable이 끝났음에도 내부 Observable은 시퀀스를 모두 내보낼 때까지는 살아있음을 알 수 있다.

모식도는 아래와 같을 듯.

TAP              --T--|>
내부 Observerable --1--|>  -2--|> 
flatMap 이후      --1--|>

 

take를 여러번 쓰는 경우

_ = Observable.from([1, 2, 3, 4, 5])
            .take(3).debug("flatMap 이전")
            .flatMap { _ in
                return Observable.from([6, 7, 8, 9, 10]).debug("내부 Observable")
            }.take(1).debug("flatMap 이후")
            .subscribe()
flatMap 이후 -> subscribed
flatMap 이전 -> subscribed
flatMap 이전 -> Event next(1)
내부 Observable -> subscribed
내부 Observable -> Event next(6)
flatMap 이후 -> Event next(6)
flatMap 이후 -> Event completed
flatMap 이후 -> isDisposed
flatMap 이전 -> isDisposed
내부 Observable -> isDisposed

flatMap 이후 부분에서 dispose됨에 따라 모든 Observable이 dispose되었다.

카테고리:

업데이트:

댓글남기기