- Observable이 전달하는 메소드(Event)가 아니다.
- 사용했었던 subscribe 메소드에서 파라미터로 클로저를 전달받게 되면 Observable과 관련된 모든 리소스가 해제된 후에 호출되는 녀석이다.
subscribe(onNext: ((Int) -> Void)?, onError: ((Error) -> Void)?, onCompleted:(() -> Void)?, onDisposed: (() -> Void)? )
이전 게시글에서 아래와 같이 두가지 방식으로 subscribe을 호출했었는데
// # 1
Observable.from([1,2,3])
.subscribe { element in
print("Next",element)
} onError: { error in
print("Error",error)
} onCompleted: {
print("Completed")
} onDisposed: {
print("Disposed")
}
// # 2
Observable.from([1,2,3])
.subscribe{
print($0)
}
이 둘을 실행 시켜보면 아래와 같은 결과가 출력된다.
결과를 비교해보면,
- #1 에서는 Disposed가 호출되었고
- #2 에서는 Disposed가 호출되지 않았다.
결론적으로 #1, #2 둘 다 해제됬다. ㅋㅅㅋ
- Observable이 Completed 메소드나 Error 이벤트로 종료되었다면 관련된 리소스가 자동으로 해제되기 때문.
그렇다면 의문점.
Disposed 메소드는 Observable의 모든 리소스가 해제될때 호출되는 녀석인데 왜 Completed, Error 이벤트로 종료가 된 후에 리소스가 해제되는 시점에 호출되지 않았을까?
- 이유는 Disposed는 Observable이 전달하는 메소드(Event)가 아니기 때문.
(Observable이 전달하는 메소드는 Next, Completed, Error 3가지)- #1 에서 Disposed 메소드가 호출되었던 이유는 onDisposed 파라미터로 클로저를 전달해 두었기 때문에 리소스가 해제되는 시점에 자동으로 호출된 것.
(#2 에서는 그런 작업이 없었으니까 Disposed 메소드가 호출되지 않은거임.)
아마.. 내 생각엔 Observable 에서 리소스가 해제되는 시점에 어떤 작업을 하고싶다면
#1 처럼 작성하고, 그런경우가 아니라면 #2처럼 작성하면 될 것 같다.
문제(..) 는 아니지만 위에서 그랬지? 정상적으로 Observable이 종료되면(Completed or Error 이벤트로) 자동으로 관련 모든 리소스가 자동으로 해제된다고..
근데 애플 공식문서를 보면 Completed 나 Error로 종료되는 경우에도
(사실 그래서 Disposed를 알아야하는거임 ㅋㅋㅋ.)
Disposable은
- 실행 취소
- 리소스 해제
에 사용되기때문
코드로 살펴보자.
let subscription1 = Observable.from([1,2,3])
.subscribe { element in
print("Next",element)
} onError: { error in
print("Error",error)
} onCompleted: {
print("Completed")
} onDisposed: {
print("Disposed")
}
subscription1.dispose()
위와 같이 dispose() 메소드를 사용해 리소스를 해제할 수 있다.
하지만 공식문서에서는 이런식으로 리소스 해제하는걸 권장하지 않는다.
왜 권장하지 않는지 실행을 취소하는 경우의 코드를 설명하면서 정리해보자.
// #1
let subscription2 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.subscribe { element in
print("Next",element)
} onError: { error in
print("Error",error)
} onCompleted: {
print("Completed")
} onDisposed: {
print("Disposed")
}
// #2
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
subscription2.dispose()
}
+코드에 대해 간단히 설명하자면,
- #1 은 1씩 증가하는 정수를 1초 간격으로 이벤트를 무한으로 방출한다.
- #2 는 이벤트를 무한으로 방출하는 #1 의 실행을 멈출 필요가 있기 때문에 dispose()메소드로 직접 중지시켰다.
실행 결과
자, 뭐가 문제라서 권장하지 않는걸까?
결과를 한번 보자.
Next 0 -> Next 1 -> Next 2 -> Disposed 순으로 이벤트가 발생했다.
어 이상한점이 있네?
바로 Observable이 마지막 이벤트까지 처리됬을때 마지막에 호출되는 Completed 가 보이지 않는다.
왜냐하면 dispose()메소드로 Observable을 강제 종료했기 때문!
이런식으로 강제 종료하게 된다면 Completed 가 호출되지 않는 등의 문제로 인해 다른 방식으로 리소스를 해제하는것을 권장한다.
이거다. DisposeBag을 이용해서 리소스를 해제해야한다.
자, 만들어보자.
var bag = DisposeBag()
Observable.from([1,2,3])
.subscribe{
print($0)
}
.disposed(by: bag) // #1
bag = DisposeBag() // #2
위의 방법처럼 disposeBag에 여러 observable의 disposable을 저장할 수 있고, disposeBag에 추가된 disposable은 disposeBag이 해지되는 시점에 함께 해지된다.
그렇다면 disposeBag의 리소스를 해제하기 위해선 어떻게 해야할까?
간단히 2가지정도 방법이 있다.
#2 처럼 새로운 DisposeBag을 할당하거나
처음부터 #2의 변수를 optional 변수로 만들어 nil을 넣어주면 된다.
결국!
이렇게 Observable이 전달하는 이벤트가 종료될 때, 모든 리소스를 해제하기 위해서는 위와 같은 방식으로 disposeBag을 이용하는 것을 권장한다. !
음..
이런느낌
Observable.from([1,2,3])
.subscribe{
print($0)
}
.disposed(by: bag) // #1