-
[RxSwift] dispose, disposed(by:), DisposeBag 역할과 매커니즘에 대해/RxSwift 2025. 3. 3. 18:01반응형
2025.02.25 - [/RxSwift] - [RxSwift] RxSwift 란 무엇일까?
2025.02.26 - [/RxSwift] - [RxSwift] RxSwift를 이루는 Observable과 Observer
2025.02.28 - [/RxSwift] - [RxSwift] “Hot” Observables, “Cold” Observables
안녕하세요 아렉스입니다. :> RxSwift에서 메모리 관리를 도와주는 DiposeBag에 대해서 얘기해 보겠습니다.
RxSwift에서 시퀀스를 적절히 종료하지 않으면 메모리 누수(Memory Leak)가 발생할 수 있습니다.
2025.02.26 - [/RxSwift] - [RxSwift] RxSwift를 이루는 Observable과 Observer에서 시퀀스의 종료에 대해서 이야기했었습니다.
위 글을 요약하자면, Observable의 시퀀스를 onError와 onCompletd 이벤트를 통해 종료할 수 있습니다.
Rx는 이 두 가지 이벤트 이외에도 dispose()를 통해 시퀀스를 종료할 수 있습니다.
이를 방지하기 위해 또한 RxSwift에서는 dispose() 이외에도 Disposable, 그리고 DisposeBag을 제공합니다.
1. dispose, Disposable, DisposeBag의 목적과 역할
1.1 dispose()의 역할
dispose()는 해당 Observable 스트림을 즉시 중단하고, 관련 리소스를 해제하는 역할을 합니다.
let interval = Observable<Int>.interval( .seconds(1), scheduler: MainScheduler.instance ) ✅ Observable 구독 let subscription = interval.subscribe(onNext: { value in print(value) }) ✅ 구독 해지 DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { subscription.dispose() // 구독을 중단하고 메모리 해제 print("disposed") }) ✅ 실행결과 // 0 // 1 // disposed
위 코드에서 dispose()를 호출하면 Observable이 더 이상 이벤트를 방출하지 않으며, 메모리에서 해제됩니다.
그렇다면 구독을 해지하는 dispose()는 어떻게 호출이 가능한 것일까요?
Observable과 Observer를 이어주는 subscribe 메서드 정의한 곳의 반환 타입을 확인하시면 됩니다.
Disposable은 구독(Subscription)을 나타내는 객체입니다. 즉, 특정 Observable을 구독한 후 이를 취소하거나 해제할 수 있는 핸들러 역할을 합니다.
일반적으로 수동으로 호출하지 않으려 한다는 점에 유의하세요 dispose. 이것은 교육적 예일 뿐입니다.
수동으로 dispose를 호출하는 것은 일반적으로 나쁜 코드 냄새입니다.
DisposeBag, takeUntil연산자 또는 다른 메커니즘과 같이 구독을 폐기하는 더 나은 방법이 있습니다.
출처: RxSwfit github doc1.2 DisposeBag의 역할
직접 수동으로 dispose() 호출하는 것을 권장하지 않는 것을 Github RxSwift 가이드에서 확인할 수 있습니다.
RxSwift에서 Swift의 ARC(자동 참조 카운트)와 유사한 동작을 DisposeBag을 통해 제공합니다.
DisposeBag은 여러 Disposable을 한 곳에 모아뒀다가 한 번에 정리할 수 있도록 도와주는 역할을 합니다.
DisposeBag이 메모리에서 해제될 때 내부의 모든 Disposable도 자동으로 dispose() 됩니다.
2. ARC처럼 동작하는 DisposeBag의 메커니즘
DisposeBag은 ARC(Automatic Reference Counting)의 인스턴스 생명주기를 활용하여 메모리 해제를 자동화합니다
- DisposeBag이 포함된 인스턴스(ViewController 등)가 해제될 때, DisposeBag도 같이 해제됩니다.
- DisposeBag이 해제되면서 내부에 저장된 모든 Disposable이 자동으로 dispose() 됩니다.
이를 통해 직접 dispose()를 호출할 필요 없이 객체의 생명주기와 함께 자동으로 정리할 수 있습니다.
DisposeBag을 활용한 메모리 자동 관리 예제
final class SampleViewController: UIViewController { // ✅ SampleViewController class 소멸시 클래스 프로퍼티 또한 소멸 private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() bind() } private func bind () { let observable = Observable.of("Hello", "RxSwift") observable.subscribe(onNext: { value in print(value) }) .disposed(by: disposeBag) // ✅ DisposeBag에 추가하여 자동 관리 } }
위 코드에서 SampleViewController가 메모리에서 해제되면, disposeBag도 함께 해제되면서 내부 Disposable들이 자동으로 dispose() 됩니다.
2.1 자동으로 dispose 되는 내부 코드로 딥다이빙 해보기
- DisposeBag이 포함된 인스턴스(ViewController 등)가 해제될 때, DisposeBag도 같이 해제됩니다.
- DisposeBag이 해제되면서 내부에 저장된 모든 Disposable이 자동으로 dispose() 됩니다.
DisposeBag이 해제가 된다는 것은 보통 deinit을 의미하기에, deinit의 동작을 확인해 보겠습니다.
구체적인 내용 파악도 중요하지만, 단계적으로 맥락상으로 파악 먼저해보겠습니다.
1. deinit: self.dispose() 호출
2. _dispose(): 안전을 고려한 동시성 처리를 하며 self.disposables을 let disposables에 할당 후 clear 해주고
let disposablesdisposables을 반환
3. dispose(): self._dispose()를 통해 전달받은 oldDisposables를 순회하며, 각 Disposables에게 dispose 호출2.2 DisposeBase
DisposeBag 은 DisposeBase를 상속받고 있습니다.
DisposeBase의 역할은 인스턴스 생성과 소멸 시점에 Resources의 개수를 증가 및 감소를 관리합니다.
확실히 Swift의 RC 메커니즘과 유사한 것을 확인할 수 있습니다.
2.3 Resources
구조체인 Resources의 역할은 AtomicInt 타입의 값의 증가 / 감소를 관리하고 있습니다.
2.3 AtomicInt
Atomic(원자성)은 동시성 프로그래밍에서 중요한 개념입니다.
연산이 중간에 다른 스레드의 방해 없이 완전히 실행되어 경쟁 상태를 방지하는 특성을 가집니다.이러한 특성을 NSLock를 상속하여 구현하고 있습니다.
func add(_:_:) 메서드를 통해 lock 메커니즘을 통해 동시성을 제어하는 것을 확인할 수 있습니다.
3. 정리
RxSwift에서 시퀀스를 적절히 종료하지 않으면 메모리 누수(Memory Leak)가 발생할 수 있습니다.
시퀀스를 적절히 종료하는 방법은 onError와 onCompletd 이벤트 방출 및 dispose() 호출을 통해 종료할 수 있습니다.
dispose()를 직접 호출하는 것은 권장하지 않는 방법이며, DisposeBag 혹은 takeUntil 연산자 사용을 권장합니다.
DisposeBag의 방식은 Swift의 ARC의 메커니즘처럼 행동합니다.
해당 메커니즘을 들여다보면 Disposable 이 disposed(by:) 메서드를 통해 DisposeBag에 추가가 될 때,
원자성을 가진 Int 타입(AtomicInt)의 값을 증가 및 감소를 하며 Disposable의 개수를 관리하고 있습니다.인스턴스의 프로퍼티로 선언된 DisposeBag의 소멸 또한 해당 인스턴스의 생명주기와 같이하기 때문에 ARC 메커니즘과 유사합니다.
https://github.com/ReactiveX/RxSwift/blob/main/Documentation/GettingStarted.md#disposing
RxSwift/Documentation/GettingStarted.md at main · ReactiveX/RxSwift
Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.
github.com
잘못된 내용 혹은 이해가 어려운 내용들은 언제든 댓글에서 의견 교환하고 싶습니다!
공감과 구독은 더 나은 글을 작성되는데 큰 도움이 됩니다.읽어주셔서 감사합니다.
' > RxSwift' 카테고리의 다른 글
[RxSwift] “Hot” Observables, “Cold” Observables (0) 2025.02.28 [RxSwift] RxSwift를 이루는 Observable과 Observer (0) 2025.02.26 [RxSwift] RxSwift 란 무엇일까 ? (0) 2025.02.25