take Operator에 대하여(RxSwift)
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되었다.
댓글남기기