RxSwift Bind에 대한 고찰
최근 작업을 하다가 갑자기 궁금한 내용이 생겼다.
간단히 요약하자면 Relay를 Relay에 Binding하였는데 이때 타겟 Relay가 초기화되면 어떻게 될까? 이다.
나는 일반적으로 bind을 할때 아래와같은 형식으로 작성한다.
var targetSubject = PublishSubject<String>()
var parentSubject = PublishSubject<String>()
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject")
}).disposed(by: disposeBag)
parentSubject.bind(to: targetSubject).disposed(by: disposeBag)
parentSubject.onNext("Hello World")
parentSubject의 onNext호출 시퀀스를 targetSubject로 바인딩 시키는건데 위 코드의 결과는 아래와같다.
parentSubject는 'Hellow World'라는 스트링을 넘겼고 그걸 targetSubject에서 받아서 출력한다.
이게 전부인 코드인데 여기서 bind이후에 targetSubject를 초기화 시키면 어떻게 될까? 가 궁금했었다.
코드로 보면 아래와 같다. 위의 코드에서 딱 2줄이 추가된다.
var targetSubject = PublishSubject<String>()
var parentSubject = PublishSubject<String>()
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject")
}).disposed(by: disposeBag)
parentSubject.bind(to: targetSubject).disposed(by: disposeBag)
parentSubject.onNext("Hello World")
// 아래 2줄 추가.
targetSubject = PublishSubject<String>()
parentSubject.onNext("second Hello World")
직관적으로 생각하기에 bind 메서드에 파라미터로 넘어간 targetSubject는 최초에 초기화된 시점의 targetSubject일텐데
아래에서 targetSubject를 초기화 시키면 기존에 bind에 넘어갔던 targetSubject와 다른값(객체의 주소값)이 되버리기 때문에 기존의 바인딩은 어떻게 되는지 확인을 해보았다.
실제 위의 코드를 호출하면 아래와같은 결과가 나온다.
그렇다. targetSubject가 초기화되도 최초에 subscribe했던 코드블럭이 동작한다.
굉장히 신기한부분이다 분명 새로 객체를 초기화했고, 이 객체는 parentSubject에 바인딩된 객체주소가 아닌데 이렇게 동작한다니.. 신기하면서 이해가 안간다.
그렇다면 한가지 더 확인해보고 싶었다.
초기화한 targetSubject의 subscribe를 새로하면 기존의 코드블럭이 동작할것인가 새로운 코드블럭이 동작할것인가!
아래와 같이 코드를 작성해보았다. 위의코드에서 targetSubject 초기화 후 Subscribe를 새로해준 코드를 추가하였다.
var targetSubject = PublishSubject<String>()
var parentSubject = PublishSubject<String>()
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject")
}).disposed(by: disposeBag)
parentSubject.bind(to: targetSubject).disposed(by: disposeBag)
parentSubject.onNext("Hello World")
targetSubject = PublishSubject<String>()
// 요기 추가.
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject but this call is New Instance")
}).disposed(by: disposeBag)
parentSubject.onNext("second Hello World")
내 예상으론 'but this call is New Instance'의 문구가 담긴 코드블럭이 호출되야 할것같았지만 결과는 반대였다.
코드를 분명 위와같이 수정하고 동작시켰지만 두번째의 결과와 동일하게 나왔다.
새로 작성한 코드블럭이 유효하게 동작하지 않고 기존의 코드블럭을 실행한다는 결과였다.
즉 객체를 초기화하던, Subscirbe 코드블럭을 새로만들던, 한번 바인딩된것을 바꿀수는 없다는것으로 보여진다.
(정확히는 새로 bind를 하지않는 이상 targetSubject를 수정한다고 해서 반영이 안된다.)
그렇다면 마지막으로 한가지만 더 확인해보았다.
초기화 하기전에 subscribe 코드블럭을 N개 생성하면 초기화 이후에 N개의 코드블럭이 모두 실행될것인가,
그리고 초기화 이후에 설정된 코드블럭들은 동작을 안하는것인가?였다.
기본적으로 Subject의 Subscribe는 여러개의 Subscribe 코드블럭 생성이 가능하다.
하나의 Subject에 줄줄이 Subscribe를 해도 정상적으로 동작한다는 뜻이다.
한번 실험해보고자 아래와 같이 코드를 작성해보았다.
var targetSubject = PublishSubject<String>()
var parentSubject = PublishSubject<String>()
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject")
}).disposed(by: disposeBag)
parentSubject.bind(to: targetSubject).disposed(by: disposeBag)
parentSubject.onNext("Hello World")
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject but this call is New Instance [1]")
}).disposed(by: disposeBag)
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject but this call is New Instance [2]")
}).disposed(by: disposeBag)
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject but this call is New Instance [3]")
}).disposed(by: disposeBag)
targetSubject = PublishSubject<String>()
targetSubject.subscribe(onNext: { item in
print("receive \(item) in targetSubject but this call is New Instance [4]")
}).disposed(by: disposeBag)
parentSubject.onNext("second Hello World")
결과는 예상했다시피 targetSubject를 초기화 하는 코드 이전에 등록된 코드블럭들은 동작하고
초기화 이후에 등록한 코드블럭은 동작하지 않았다.
'but this call is New Instance [N]'중 1,2,3의 코드블럭은 동작하였으나 4의 코드블럭은 동작하지 않은것을 볼수있다.
문서를 읽어봐야 알겠지만 추측하기론 Subscribe동작은 최초에 생성된 객체에 등록된 코드블럭만 유효한것으로 보여진다.
또한 bind은 타겟 Subject를 한번 등록하면 그 Subject가 초기화가 되어 주소값이 변경되더라도 계속 바인딩을 유지하는것으로 보여진다.
상용화 단계에서 bind를 사용하고 타겟 Subject의 초기화가 반드시 필요하다면
최초 Subject생성시점과 초기화시점사이에 동작해야할 Subscribe 코드블럭을 모두 등록해야하는것으로 보여진다.
초기화가 이루어진뒤 Subscribe코드 블럭을 추가할것이라면 해당 코드블럭 관련 DisposeBag을 초기화 시키고 bind를 새로해야할것같다.