Disposables

hzw94·2021년 5월 4일
0

RxSwift

목록 보기
2/10

알아보기 전에 3개 정수를 방출하는 Observable을 만들어보자.

Observable.from([1,2,3])

그 뒤에 구독(subscribe)를 하고, 각 이벤트가 전달 될 때 필요한 규약을 작성한다.

Observable.from([1,2,3])
    .subscribe(onNext: { elem in
        print("Next", elem)
    }, onError: { error in
        print("Error", error)
    }, onCompleted: {
        print("Completed")
    }, onDisposed: {
        print("Disposed")
    }
)
/이것은 어떠한 이벤트일지? Observable 이 전달하는 이벤트는 아님.
        // 파라미터로 closure를 전달하면 Observable가 모두 제거된 이후에 호출이된다.

위의 코드에서

onDisposed: {
        print("Disposed")
    }

를 보면 이게 무엇을 하는지, 언제 호출이 되는지 궁금할 것이다.
일단 이것은 Observable이 전달하는 이벤트는 아니다.
Parameter로 Closure를 전달하면 Observable이 모두 제거된 이후에 호출이 되는 특징이 있다.

그렇다고 하여, 모든 Observable에서 나타나지는 않는다. 위의 코드에서 onDisposed에 대해 정의 해있고 메모리에서 해제되는 시점에 어떠한 Event를 수행하기에 처리된 것 뿐이다.

일반적으로 프로그램이 Completed, Error에 의해 사망한다면 메모리에서 프로그램 역시 해제되기에 딱히 큰 문제는 없다. 하지만, RxSwift에서는 메모리에서 리소스를 제대로 정리해야하는 스타일이 존재하므로 일반적으로는 사용해서 메모리를 정리해주도록 하자.

이제 리소스 해제에 사용되는 disposed를 보도록하자.

let subscription1 = Observable.from([4,5,6])
    .subscribe(onNext: { elem in
        print("Next", elem)
    }, onError: { error in
        print("Error", error)
    }, onCompleted: {
        print("Completed")
    }, onDisposed: {
        print("Disposed")
    })

상수에 Observable을 저장한 다음,

subscription1.dispose()

로 해제 할수 있다.

그러나 우리는 위에 했던 방식처럼 dispose를 직접 호출하여 메모리 해제를 할수도 있지만
RxSwift는 위의 방법을 권장하지 않는다.
그러면 대체 어떻게 dispose를 하라는 것일까?

Dispose Bag을 사용하자.

DisposeBag을 만드는 방식은 간단하다.

var bag = DisposeBag()

위와 같이 bag 변수를 만들어 놓고 한번에 Dispose를 담았다가 전부 해제할수 있게된다.

그래서 위의 DisposeBag을 사용하는 방법은 다음과 같은데

Observable.from([7,8,9])
    .subscribe{
        print($0)
    }
    //parameter로 disposebag을 전달하면
    //subscribe가 리턴하는 disposable이 disposebag에 추가가 된다.
    .disposed(by: bag)

.dispose 메소드를 쓸수 있고, parameter로 disposeBag을 담은 bag을 전달하면 subscribe가 리턴하는 Disposable이 disposebag -> bag에 추가 된다.
disposebag에 추가된 dispose들은 disposebag이 해제되는 시점에 함께 소멸된다.

그렇다면 이제 또 질문이 하나생길수 있는게

그러면 내가 원하는 시기에 해제를 못하는것인지?

bag = DisposeBag()

위와 같이 기존에 var로 선언되어 있던 bag을 DisposeBag으로 초기화 시켰는데, 이렇게 기존에 쓰던 DisposeBag을 특정 시점에 초기화하거나 하게된다면 질문에서 원하던 원하는 시기에 해제가 가능하게 된다.

다시 새로운 subscription2를 만들어 보자.

let subscription2 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe(onNext: { elem in
        print("Next", elem)
    }, onError: { error in
        print("Error", error)
    }, onCompleted: {
        print("Completed")
    }, onDisposed: {
        print("Disposed")
    })

위의 subscription2는 제약이 없다면 계속 증가하는 값을 이벤트로 주게된다.
그렇다면 언제 끌수있을지 예제 코드로 알아보면 다음과같다.

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
    subscription2.dispose()
}

위와 같이 10초 간격으로 방출한 뒤에, subscription2를 dispose로 초기화 시켜주게 된다.

Dispose를 호출하는 순간 모든 Resource가 해제되기 때문에 더이상 이벤트가 전달되지 않고 Next다음에 Completed가 전달이 되지 않는다.
이러한 이유로 dispose메소드를 직접 호출하는것은 피해야 하며 만약 특정시점에 해제 해야한다면 take untail과 같은 연산자로 구현이 가능하다.

profile
볼가놈의 iOS & Swift & RxSwift & PS 저장창고

0개의 댓글