본문 바로가기

iOS (스파르타)

RxSwift 공부하기 - 2 (.. 재작성 완)

Dispose / Disposable / DisposeBag 이해하기

 

1. Disposable과 dispose()

 

Dispose는 처리하다/없애다 라는 뜻을 가지고 있는데 그럼 뭘 처리하느냐?

 

관찰 가능한 형태 Observable이 있을 때 Observer는 이 Observable을 구독을 통해 이벤트를 받을 수 있다 했다.

(정확히는 Observer가 Observable이 방출하는 항목에 대해 받는 것)

만약 더이상 이 Observable에 대한 이벤트를 받고 싶지 않아서 구독을 해제 하고 싶을 때

사용할 수 있는 것이 바로 Disposable이다.

 

이전 포스팅에서 Subscribe라는 메서드를 통해

Observer가 Observable을 구독할 수 있다는 것을 알게 되었는데

Subscribe 메서드 원형을 다시 살펴보면

 

 


... 아 적어놓은거 날라감 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

하... 오바다 진심...


 

반환 타입이 Disposable인 것을 볼 수 있다.

즉, Subscribe 메서드를 호출할 경우 Observable을 구독할 수 있지만

해당 Observable의 이벤트가 더이상 필요하지 않을 때

해당 구독을 취소할 수 있는 수단인 Disposable이란 타입의 값을 리턴하여 준다는 말이다.

 

리턴 타입인 Disposable을 살펴보면

이렇게 dispose라는 메서드를 가진 Protocol로 정의되어 있다.

 

Observer는 Observable을 구독하기 위해 Subscribe라는 메서드를 사용하는데

Subscribe 메서드는 구독을 해제할 때 사용하는 Disposable이란 타입의 인스턴스를 반환하는 거고

이 리턴된 Disposable타입의 값에 대고 dispose() 메서드를 호출하면

우리가 구독한 Observable에 대해 구독을 해제할 수 있겠다?

 

Observer가 어떤 Observable의 이벤트를 받고 싶을 경우, Subscribe 메서드로 "구독"을 통해 할 수 있고

더이상 이벤트를 받고 싶지 않아 "구독을 해제"할 경우를 대비해서

Subscribe 메서드는 Disposable이란 타입의 리턴 값을 준다.

이 Disposable은 프로토콜 타입으로 dispose 메서드를 가지고 있는데.

만약 Obserevable이 방출하는 이벤트를 더 이상 받고 싶지 않은 경우

Subscribe를 했을 때 얻은 이 Disposable에 대고 dispose() 메서드를 호출할 경우

해당 Observable에 대한 구독을 해제 할 수 있다.

 

dispose 메서드를 실행하는 이유는 원하는 시점에 해당 이벤트의 구독을 끊기 위함도 있지만

가장 중요한 이유 중 하나는 메모리 관리를 위해서 이다.

 

Observable은 기본적으로 complete 나 error가 발생하기 전까지 계속 이벤트를 방출 시키는데

따라서 이벤트가 더 이상 방출되면 안되는 시점에서 이 리소스를 직접 deinit 해줘야 한다.

만약 이 deinit 해주는 과정을 해주지 않으면 이 리소스는 계속해서 이벤트를 방출시키는 메모리 누수(leak)으로 이어진다.

 

어떻게 해당 Observable과 관련된 리소스를 deinit 시킬 수 있는가 하면

위에서 말한 dispose() 메서드를 사용하는 것이다.

 

Disposable 타입의 인스턴스의 dispose 메서드를 호출한다는 것은

해당 Observable에 대한 리소스를 deinit 시킨다는 것이다.

즉, Rx에서 메모리관리를 한다는 말이다.

 

따라서 Rx에서 이 Dispose는 메모리와 관련이 있기 때문에 아주 중요한 메서드이다.

 

Observer의 이벤트를 실행하던 ViewController가 앱에서 사라졌음에도 불구하고 

Observer에 대한 리소스가 해제되지 않으면 죽지않고 계속 이벤트를 방출하는 메모리릭이 발생한다.

이때 Observable을 생성해서 Disposable을 갖고있던 ViewController는 이미 deinit되어버려 해당 리소스를 해제할 수 있는 방법이 없다.)

 

따라서 이벤트를 방출하는 Observer를 가지고 있는 ViewController가 deinit되는 시점에 

해당 Disposable에 대한 dispose를 통해 Observable 리소스에 대해서도 같이 deinit을 실행시켜주면

ViewController가 deinit 될 때 Observable 리소스에 대한 deinit도 같이 이루어지기 때문에 메모리릭이 나지 않는다.

 

 

2. DisposeBag

DisposeBag은 말그대로 Disposable을 담는 Bag이다.

 

위에서 해당 Observable 리소스가 해제되어야 할 시점에 dispose를 호출해줘야 한다고 했다.

그런데 만약 해제해줘야하는 리소스가 많아지면 지저분한 전역변수와 코드가 늘어날 것이다.

그래서 이 Disposable들을 담는 배열을 만들어 주는것이 DisposeBag이다.

RxSwift에서 제공하는 이 DisposeBag이란 것은

쉽게 말해 Disposable 객체를 담는 배열이다.

 

전역 변수로 disposeBag 하나를 선언해주고

Disposable 프로토콜의 기본 메서드로 제공하는 이 disposed(by bag:DisposeBag) 메서드를 이용해서 담는다.

 

subscribe를 통해 배출된 Disposable 객체에 대고

disposed(by bag:) 메서드로 미리 선언된 disposeBag 객체를 넘겨주면,

disposeBag 안에 해당 Disposable이 담기게 된다.

 

그럼 이제 deinit되어야 할 때 disposeBag 안에 들어 있는 Disposable 객체들을 다 dispose() 시켜야한다는 생각이 들텐데

실제로는 deinit 때 아무런 작업을 해주지 않아도 메모리 릭이 일어나지 않는다.

 

이는 DisposeBag 객체를 보면 되는데

위처럼 DisposeBag 객체 자체가 deinit되는 순간 내부에 있는 dispose() 메서드를 호출하는데

이 메서드에서 해당 Disposables란 Disposable 배열에 담긴

disposable을 순회하면서 모두 dispose() 메서드를 호출해주기 때문이다.

 

정리하면

DisposeBag을 전역 프로퍼티로 선언해준 경우,

(다른 곳에서 참조하지 않은 경우) 해당 DisposeBag을 담고있는 인스턴스가 사라질 때

DisposeBag이란 프로퍼티도 같이 deinit 될 것이고

이때 내가 갖고 있는 Disposable의 배열을 순회하며 dispose() 메서드를 호추해주기 때문에

딱히 다른 작업을 해주지 않아도 DisposeBag이 갖고있는 Disposable들의 메모리 릭이 나지 않을 수 있는 것이다.

(만약 특정 상황에 disposeBag을 직접 비워주어야하는 상황이면 해당 상황에 맞게 disposeBag을 메모리에서 해제해주면 된다.)

 

disposeBag은 자신이 deinit 될 때 자신이 갖고 있던 해당 Disposable을 모두 dispose() 시켜버리기 때문에

만약 disposeBag을 전역이 아닌 viewDidLoad 메서드 안의 지역번수로 선언할 경우,

viewDidLoad 메서드가 끝날 때 disposeBag도 같이 메모리에서 해제되기 때문에

이때 등록되어있던 Observable 또한 다같이 구독 해제 되어 버려 Observable이 작동하지 않을 것이다.

 

 

3. Subscribe 할 때 마지막 클로저 onDisposed: 는

이전 Subscribe 메서드를 설명할 때

onDisposed는 Disposable에 대해 설명하고 하기위해 생략했는데 이제 disposed를 공부했으니 이해가 갈 것이다.

 

해당 Observable이 구독 해제될 때

즉 disposed() 메서드가 불릴 때 실행 될 클로저를 넘겨주는 것이다.

(onError나 onComplete로 인해 disposed 될 때도 당연히 실행 된다.)

 

 

4. ControlEvent의 경우, 해당 이벤트를 갖고 있는 인스턴스가 deinit 될 때 자동으로 dispose 된다.

ControlEvent를 통해 Observable을 구독할 때

따로 dispose를 해주지 않았음에도 자동으로 disposed() 메서드가 불리며, onDisposed: 에 등록해놓은 클로저가 불린다.

 

이부분은 잘 이해가 안가긴 함..

ControlEvent의 경우 자동으로 dispose된다고 해도 disposeBag에 넣는게 좋아보임!

 

'iOS (스파르타)' 카테고리의 다른 글

JSON 을 알아보쟈,,  (1) 2024.09.30
아악! 공부해야할것  (0) 2024.09.19
파이어베이스 공부하깅 - 4  (0) 2024.09.13
파이어베이스 공부하깅 - 3  (0) 2024.09.12
파이어베이스 공부하깅 - 2  (1) 2024.09.11