Memory Management
🧹 Memory Management — DisposeBag 패턴 & Leak 예방
“Rx는 clean‑up이 전부다.”
Reactive 스트림은 강력하지만, 구독 해제(Dispose)가 누락되면 메모리 누수·배터리 소모·예상치 못한 콜백이 이어집니다. 이 문서에서 DisposeBag 활용, Retain Cycle 차단, Leak 탐지 도구를 정리합니다.
1️⃣ DisposeBag 101
class MyViewController: UIViewController {
private let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.output
.observe(on: MainScheduler.instance)
.bind(to: label.rx.text)
.disposed(by: bag) // ✅
}
}
원리:
DisposeBag
이 deinit 되면 내부 Disposables의dispose()
자동 호출.스코프: VC·ViewModel별 1개, 테스팅 시
TestDisposeBag()
재사용.
resetBag 패턴
var bag = DisposeBag()
func resetRx() { bag = DisposeBag() } // 모든 구독 일괄 해제
2️⃣ Retain Cycle 방지 — [weak self]
[weak self]
button.rx.tap
.withUnretained(self) // RxSwift 6 util
.subscribe(onNext: { vc, _ in
vc.performAction()
})
.disposed(by: bag)
withUnretained(self): 튜플(weakSelf, value) 반환 → 언랩 필요 X.
전통 구문:
.subscribe { [weak self] _ in self?.action() }
.Subject·Relay 내부에
self
저장 금지.
3️⃣ Leak 탐지 툴
Xcode Memory Graph
실시간 인스턴스 그래프
Debug ▸ View Memory Graph (⇧⌘I
)
Instruments ‑ Allocations
라이브 객체 카운트
Filter by ClassName, Track Live
RxLeakDetector
RxCommunity · 자동 Dispose 검사
RxSwiftExt+LeakDetector
설치 후 로그 확인
WeakProxy
Timer/Delegate Retain cut
WeakProxy(target)
패턴
4️⃣ Long‑lived Streams 관리
Interval/Timer
.takeUntil(dealloc)
or bag dispose
BLE / WebSocket
ViewModel Scope + Subject
stop on app background
NotificationCenter.rx
.takeUntil(self.rx.deallocated)
자동 구독 해제
5️⃣ Common Smells & Fixes
Global DisposeBag
객체 deinit 후에도 이벤트 수신
Bag scope를 객체로 제한
Nested subscribe
중첩 클로저로 다중 dispose 필요
flatMap
/ Operator chain 이용
share(scope:.forever) 남발
Hot 공유가 앱 종료까지 유지
scope 검토 or manual connect/disconnect
6️⃣ Mini Checklist ✅
7️⃣ Mini Quiz
PublishSubject
를 뷰컨트롤러 프로퍼티로 보관하면 VC가 deinit되어도 스트림이 살아있을까?Driver
사용 시 DisposeBag이 필요 없는 경우가 있을까?Xcode Memory Graph에서 Retain Cycle을 확인하는 세 단계는?
다음 ▶️ patterns.md 로 이동해 MVVM, Coordinator 등 아키텍처와 Rx 통합 전략을 살펴봅니다. 🚀
Last updated