5. Disposable?

What is Disposable?

Disposable의 사전적 의미 → “처리할 수 있는”

subscribe 메소드는 Disposable을 리턴한다

특정 객체를 구독하고 있다가 구독중인 이벤트가 onError, onCompleted로 끝나게 된다면 해당 구독은 자동으로 메모리에서 해제가 되지만, 정상적으로 진행되고 있는 이벤트 들은 직접 메모리에서 해제를 시켜줘야하는데, 이를 도와주는 것이 Disposable이다

Why Disposable?

Disposable은 Observable과 관련된 리소스를 모두 해제시켜 메모리 누수를 없애기 위해 사용한다

unused_disposable

이와같은 경고가 뜨는 이유는 subscribe메서드가 리턴해주는 Disposable객체를 사용해주지 않았기 때문이다

Observable<String>.create { observer in
    observer.onNext("Hello")
    observer.onNext("RxSwift")
    
    return Disposables.create {
        print("Observable Disposed!!")
    }
}.subscribe {
    print($0)
}

// next(Hello)
// next(RxSwift)

해당 Observable에서 onError나 onCompleted이벤트를 방출하지 않거나, dispose(), 또는 disposeBag(by:)를 호출하지 않으면 이 Observable은 영원히 메모리에서 해제되지 않는다

How to use?

1. dispose()

  • 원하는 시점에 호출하여 Observable을 중지시킬 수 있다
Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe {
        print("Next:", $0)
    } onError: {
        print("Error", $0)
    } onCompleted: {
        print("Completed")
    } onDisposed: {
        print("Disposed")
    }
    
// Next: 0
// Next: 1
// Next: 2
// Next: 3
// .....

해당 Ovservable은 dispose를 호출하지 않았기 때문에 영원히 1초마다 next이벤트를 방출한다

여기서 subscribe뒤에 바로 dispose()를 호출하게된다면

Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe {
        print("Next:", $0)
    } onError: {
        print("Error", $0)
    } onCompleted: {
        print("Completed")
    } onDisposed: {
        print("Disposed")
    }.dispose()
    
// Disposed

이렇게 아무런 이벤트도 방출하지 않고 바로 Disposed가 된다 Observable이 정상적으로 종료되지 않고 dispose()를 통해 종료됐기 때문에 completed이벤트도 방출하지 않는다

example code

let intervalObservable =  Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe {
        print("Next:", $0)
    } onError: {
        print("Error", $0)
    } onCompleted: {
        print("Completed")
    } onDisposed: {
        print("Disposed")
    }

DispatchQueue.global().asyncAfter(deadline: .now() + 4) {
    intervalObservable.dispose()
}

// Next: 0
// Next: 1
// Next: 2
// Disposed

이렇게 원하는 시점에 dispose()를 호출하여 메모리에서 해제 시킬수 있다

하지만 이러한 방식은 completed이벤트와 error이벤트를 방출하지 않기 때문에 자주 사용되진 않는다

2. DisposeBag

  • Disposable들을 DisposeBag에 담아두었다가 DisposeBag이 메모리에서 해제될때 한번에 처리하는 방식
  • 매번 호출하는 dispose()보다 편리하고 안전하여 더 자주 사용되는 방식이다

example code

private let disposeBag = DisposeBag()

Observable<Int>
    .from([1, 2, 3])
    .subscribe {

        print($0)

    }.disposed(by: disposeBag)

// next(1)
// next(2)
// next(3)
// completed

사용법은 매우 간단하다.

DisposeBag 객체를 생성한 뒤, subscribe를 호출하고 이후에 disposed(by:)를 호출하여 파라미터에 생성한 disposeBag을 넣어준다

그럼 해당 disposeBag을 소유한 인스턴스가 메모리에서 해제될때 같이 해제가 된다


References

RxSwift - Disposable

RxSwift Disposable 이란?

순한맛 RxSwift [5] - Disposable