RxSwift - Dispose, Disposable, DisposeBag
ย Disposable๊ณผ dispose()
Dispose - v. ์ฒ๋ฆฌํ๋ค/์์ ๋ค
๊ด์ฐฐ ๊ฐ๋ฅํ ํํ Observable์ด ์์ ๋, Observer๋ ์ด Observable์ โ๊ตฌ๋
โ์ ํตํด ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ ์๋ค๊ณ ํ๋ค.
โ ์ ํํ๋ Observer๊ฐ Observable์ด ๋ฐฉ์ถํ๋ ํญ๋ชฉ์ ๋ํด ๋ฐ๋ ๊ฒ
๋ง์ฝ ๋์ด์ ์ด Observable์ ๋ํ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ถ์ง ์์์ โ๊ตฌ๋
์ ํด์ โ ํ๊ณ ์ถ์ ๋๋? โ Disposable
ย Subscrib ๋ฉ์๋ ์ํ
์ด๋ ๊ฒ ๋ฐํ ํ์
์ด Disposable ์ธ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ฆ, subscribe ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฒฝ์ฐ, Observable์ ๊ตฌ๋
ํ ์ ์์ง๋ง, ํด๋น Observable์ ์ด๋ฒคํธ๊ฐ ๋ ์ด์ ํ์ํ์ง ์์ ๋, ํด๋น ๊ตฌ๋
์ ์ทจ์ํ ์ ์๋ ์๋จ์ธ Disposable ์ด๋ ํ์
์ ๊ฐ์ ๋ฆฌํดํ์ฌ ์ค๋ค.
์ด์ ๋ฆฌํด ํ์
์ธ Disposable ์ ์ดํด๋ณด๋ฉด
public protocol Disposable {
func dispose()
}
Swift
๋ณต์ฌ
์ด๋ ๊ฒ dispose๋ผ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง Protocol๋ก ์ ์๋์ด ์๋ค.
Observer๋ Observable์ ๊ตฌ๋
ํ๊ธฐ ์ํด Subscribe๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด Subscribe ๋ฉ์๋๋ ๊ตฌ๋
์ ํด์ ํ ๋ ์ฌ์ฉํ๋ Disposable์ด๋ ํ์
์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค!
๊ทธ๋ฌ๋ฉด ์ด ๋ฆฌํด๋ Disposable ํ์
์ ๊ฐ์ ๋๊ณ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ์ฐ๋ฆฌ๊ฐ ๊ตฌ๋
ํ Observable์ ๋ํด ๊ตฌ๋
์ ํด์ ํ ์ ์๋ค!
let hjDisposable = hjButton.rx
.tap
.subscribe(onNext: {
print("hj ๋ฒํผ ๋๋ฆผ!")
})
hjDieButton.rx
.tap
.subscribe(onNext: {
hjDisposable.dispose()
print("hj ๋ฒํผ ๊ตฌ๋
ํด์ !")
})
Swift
๋ณต์ฌ
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด, hjButton์ tap ์ด๋ฒคํธ์ ๋ํด subscribe๋ฅผ ํตํด ๊ตฌ๋
์ ํ๊ณ , ํด๋น ๊ตฌ๋
์ ์ทจ์ํ ์ ์๋ ๋ฆฌํด ๊ฐ, ์ฆ Disposable ์ธ์คํด์ค๋ฅผ hjDisposable์ด๋ ๋ณ์์ ์ ์ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ hjDieButton์ด ๋๋ ธ์ ๋, ์ด hjDisposable์ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํด๋ณธ๋ค.
hjDieButton์ด ๋๋ฆฌ๊ธฐ ์ ๊น์ง ์ ๋์ํ๋ hjButton์ tap ์ด๋ฒคํธ๊ฐ, hjDieButton์ด ๋๋ฆฌ๋ ์๊ฐ hjButton์ tap Observable์ ๋ํ ๊ตฌ๋
์ ํด์ ํด์ฃผ๊ธฐ ๋๋ฌธ์, ๋์ด์ hjButton์ tap ์ด๋ฒคํธ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํ ์๊ฐ ์์ด์ง๋ค.
๋ฐ๋ผ์ ๊ตฌ๋
ํด์ ํ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋์ด์ ์๋ฌด๋ฐ ์ด๋ฒคํธ๋ฅผ ํ์ง ์๊ฒ ๋๋ค.
ย ์์ฝ
Observer๊ฐ ์ด๋ค Observable์ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ถ์ ๊ฒฝ์ฐ, Subscribe ๋ฉ์๋๋ก โ๊ตฌ๋
โ์ ํตํด ํ ์ ์๋ค.
ํ์ง๋ง ๋์ด์ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ถ์ง ์์์ โ๊ตฌ๋
์ ํด์ โ ํ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด Subscribe ๋ฉ์๋๋ Disposable์ด๋ ํ์
์ ๋ฆฌํด ๊ฐ์ ์ค๋ค.
์ด Disposable์ ํ๋กํ ์ฝ ํ์
์ผ๋ก dispose ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ, ๋ง์ฝ Observable์ด ๋ฐฉ์ถํ๋ ์ด๋ฒคํธ๋ฅผ ๋์ด์ ๋ฐ๊ณ ์ถ์ง ์์ ๊ฒฝ์ฐ,
Subscribe๋ฅผ ํ์ ๋ ์ป์ ์ด Disposable์ ๋๊ณ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฒฝ์ฐ, ํด๋น Observable์ ๋ํ ๊ตฌ๋
์ ํด์ ํ ์ ์๋ค.
์ฌ์ค dispose ๋ฉ์๋๋ฅผ ์คํํ๋ ๊ฑด, ํด๋น ์์ (๋ด๊ฐ ์ํ๋ ์์ )์ ํด๋น ์ด๋ฒคํธ์ ๊ตฌ๋
์ ๋๊ธฐ ์ํ ๊ฒ๋ ์์ง๋ง, ๊ฐ์ฅ ์ค์ํ ์ด์ ์ค ํ๋๋ ๋ฐ๋ก
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
๋ฅผ ์ํด์์ด๋ค. Observable์ ๊ธฐ๋ณธ์ ์ผ๋ก, complete๋ error๊ฐ ๋ฐ์ํ๊ธฐ ์ ๊น์ง ๊ณ์ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ ์ํค๋๋ฐ, ๋ฐ๋ผ์ ์ด๋ฒคํธ๊ฐ ๋์ด์ ๋ฐฉ์ถ๋๋ฉด ์๋๋ ์์ ์์ ์ฐ๋ฆฌ๋ ์ด ๋ฆฌ์์ค๋ฅผ ์ง์ deinit ํด์ค์ผ ํ๋ค.
๋จ์ฝ deinit ํด์ฃผ๋ ๊ณผ์ ์ ํ์ง ์์ผ๋ฉด, ์ด ๋ฆฌ์์ค๋ ๊ณ์ํด์ ํ์ํ ๋๋ง๋ค ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ์ํค๋ ๋ฉ๋ชจ๋ฆฌ ๋ฆญ(leak) ์ผ๋ก ์ด์ด์ง๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ฉด ์ด๋ป๊ฒ ํด๋น Observable๊ณผ ๊ด๋ จ๋ ๋ฆฌ์์ค๋ฅผ deinit ์ํฌ ์ ์์๊น? โ dispose() ๋ฉ์๋!
Disposable ํ์
์ ์ธ์คํด์ค์ dispose ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ ๊ฒ์ ํด๋น Observable์ ๋ํ ๋ฆฌ์์ค๋ฅผ deinit ์ํจ๋ค๋ ๊ฒ์ด๋ค. ์ฆ, Rx์์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ํ๋ค๋ ๊ฒ
โ ์ด๋ ๊ฒ Rx์์ ์ด dispose๋ ๋ฉ๋ชจ๋ฆฌ์ ๊ด๋ จ์ด ์๊ธฐ ๋๋ฌธ์ ์์ฃผ ์ค์ํ ๋ฉ์๋์ด๋ค.
์๋ฅผ๋ค๋ฉด
class HjViewController: UIViewController {
override func viewDidLoad() {
super.veiwDidLoad()
let _ = Observable<Int>.interval(
.seconds(1),
scheduler: MainScheduler.instance
)
.subscribe(onNext: {
print($0)
})
}
}
Swift
๋ณต์ฌ
HjViewController๊ฐ ์คํ๋๋ฉด, 1์ด์ ํ๋ฒ์ฉ 0๋ถํฐ ์์ฐจ์ ์ธ ์ซ์๋ฅผ ๋ฐฉ์ถํ๋ Observable์ ๋ง๋ค๊ณ ,
ํด๋น Observable์ ๊ตฌ๋
ํ์ฌ ์ด๋ฒคํธ๊ฐ ๋ฐฉ์ถ๋์ ๋, ๊ทธ ์ซ์๋ฅผ ์ถ๋ ฅํ๊ฒ ํ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ๊ตฌ๋
์ ํตํด ๋ฆฌํด๋ฐ์ Disposable ํ์
์ ์ธ์คํด์ค๋ก๋ ์๋ฌด๊ฒ๋ ํ์ง ์๋๋ค.
์ด๋ ๊ฒ ๋ ๊ฒฝ์ฐ, ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธฐ๋๋ฉด
HjViewController๊ฐ ์์ฑ๋์ด ๋์์ก์ ๋, 1์ด์ ํ๋ฒ์ฉ ์์ฐจ์ ์ธ ์ซ์๋ฅผ ๋ฐฉ์ถํ๋ Observable์ด ์์ฑ๋๊ณ , ํด๋น Observable์ด ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ ํ์ ๋ subscribe ํด๋ก์ ์ ์ํด 0, 1, 2๊ฐ ์ฐํ๊ณ ์๋ค.
ํ์ง๋ง 2๊น์ง ์ฐํ๊ณ ๋์ HjViewController๋ฅผ ๋ด๋ ค์, ํด๋น Observer์ ์ด๋ฒคํธ๋ฅผ ์คํํ๋ ViewController๊ฐ ์ฑ์์ ์ฌ๋ผ์ก์์๋ ๋ถ๊ตฌํ๊ณ , ํด๋น Obserer์ ๋ํ ๋ฆฌ์์ค๊ฐ ํด์ ๋์ง๊ฐ ์์์, ์ฃฝ์ง์๊ณ ๊ณ์ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๊ณ ์๋ ๋ฉ๋ชจ๋ฆฌ leak๊ฐ ๋ฐ์ํ๊ณ ์๋ค.
๋ฐ๋ผ์
class HjViewController: UIViewController {
private var timerDisposable: Disposable?
override func viewDidLoad() {
super.viewDidLoad()
timerDisposable = Observable<Int>.interval(
.seconds(1)
scheduler: MainScheduler.instance
)
.subscribe(onNext: {
print($0)
})
}
deinit {
timerDisposable?.dispose()
}
}
Swift
๋ณต์ฌ
๋๋ฅผ ๊ฐ์ง๊ณ ์๋ ViewController๊ฐ deinit ๋๋ ์์ ์ ํด๋น Disposable์ ๋ํ dispose๋ฅผ ํตํด ํด๋น Observable ๋ฆฌ์์ค์ ๋ํด์๋ ๊ฐ์ด deinit์ ์คํ์์ผ์ฃผ๋ฉด
ViewController๊ฐ deinit ๋ ๋ ํด๋น Observable ๋ฆฌ์์ค์ ๋ํ deinit๋ ๊ฐ์ด ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ๋ฉ๋ก๋ฆฌ ๋์๊ฐ ์ผ์ด๋์ง ์๋๋ค.
ย DisposeBag
๋ง ๊ทธ๋๋ก Disposable์ ๋ด๋ Bag์ด๋ค.
Observable ๋ฆฌ์์ค๊ฐ ํด์ ๋์ด์ผ ํ ์์ ์ dispose๋ฅผ ํธ์ถํด์ฃผ์ด์ผ ํ๋ค๊ณ ํ๋ค.
ํ์ง๋ง ๋ง์ฝ ๋ค์๊ณผ ๊ฐ์ด, ํด์ ํด์ค์ผ ํ๋ ๋ฆฌ์์ค๊ฐ ๋ง์์ง๋ค๋ฉด,
class HjViewController: UIViewController {
private var timerDisposable: Disposable?
private var button1Disposable: Disposable?
private var button2Disposable: Disposable?
...
deinit {
timerDisposable?.dispose()
button1Disposable?.dispose()
button2Disposable?.dispose()
}
}
Swift
๋ณต์ฌ
์ด๋ ๊ฒ ์ง์ ๋ถํด์ง๊ฒ์ด๋ค.
๊ทธ๋์ Disposable๋ค์ ๋ด๋ ๋ฐฐ์ด์ ๋ง๋ ๋ค โ DisposableBag
public final class DisposeBag: DisposeBase {
private var disposables = [Disposable]()
Swift
๋ณต์ฌ
RxSwift์์ ์ ๊ณตํ๋ ์ด DisposeBag ์ด๋ ๊ฒ์ ์ฝ๊ฒ ๋งํด์ Disposable ๊ฐ์ฒด๋ฅผ ๋ด๋ ๋ฐฐ์ด์ด๋ค.
class HjViewController: UIViewController {
private var disposeBag: DisposeBag = .init()
}
Swift
๋ณต์ฌ
์์ฒ๋ผ, ์ ์ญ ๋ณ์๋ก disposeBag ํ๋๋ฅผ ์ ์ธํด์ฃผ๊ณ , disposable ๊ฐ์ฒด๋ฅผ ์ด์ ์ด๋ป๊ฒ ๋ด๋๋ฉด,
extension Disposable {
public func disposed(by bag: DisposeBag) {
bag.insert(self)
}
Swift
๋ณต์ฌ
Disposable ํ๋กํ ์ฝ์ ๊ธฐ๋ณธ ๋ฉ์๋๋ก ์ ๊ณตํ๋ ์ด disposed(by bag: DisposeBag) ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ๋ด์ ๊ฒ์ด๋ค.
class HjViewController: UIViewController {
private var disposeBag: DisposeBag = .init()
override func viewDidLaod() {
super.viewDidLoad()
Observable<Int>.interval(
.seconds(1),
scheduler: MainScheduler.instance
)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}
}
Swift
๋ณต์ฌ
์์ฒ๋ผ, subscribe๋ฅผ ํตํด ๋ฐฐ์ถ๋ Disposable ๊ฐ์ฒด์ ๋๊ณ disposed(by bag:) ๋ฉ์๋๋ก ๋ฏธ๋ฆฌ ์ ์ธ๋ disposeBag ๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ฃผ๋ฉด, ํด๋น disposeBag ์์ ํด๋น Disposable์ด ๋ด๊ธฐ๊ฒ ๋๋ค.
๊ทธ๋ฌ๋ฉด, deinit ๋์ด์ผ ํ ๋, disposeBag ์์ ๋ค์ด์๋ Disposable ๊ฐ์ฒด๋ค์ ๋ค dispose() ์์ผ์ผํ๋?
๊ทผ๋ฐ, ๊ตณ์ด deinit ๋ ์๋ฌด๋ฐ ์์
์ ํด์ฃผ์ง ์์๋ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ์ผ์ด๋์ง ์๋๋ค.
public final class DisposeBag: DisposeBase {
deinit {
self.dispose()
}
Swift
๋ณต์ฌ
์์ฒ๋ผ, DisposeBag ๊ฐ์ฒด ์์ฒด๊ฐ deinit ๋๋ ์๊ฐ, ๋ด๋ถ์ ์๋ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
private func dispose() {
let oldDisposables = self._dispose()
for disposable in oldDisposables {
disposable.dispose()
}
}
Swift
๋ณต์ฌ
์ด ๋ฉ์๋์์ ํด๋น Disposables๋ Disposable ๋ฐฐ์ด์ ๋ด๊ธด disposable์ ์ํํ๋ฉด์ ๋ชจ๋ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํด์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค.
ย ์์ฝ
DisposeBag์ ์ ์ญ ํ๋กํผํฐ๋ก ์ ์ธํด์ค ๊ฒฝ์ฐ,
(๋ค๋ฅธ ๊ณณ์์ ์ฐธ์กฐํ์ง ์์ ๊ฒฝ์ฐ) ํด๋น DisposeBag์ ๋ด๊ณ ์๋ ์ธ์คํด์ค๊ฐ ์ฌ๋ผ์ง ๋, DisposeBag์ด๋ ํ๋กํผํฐ๋ ๊ฐ์ด deinit์ด ๋ ๊ฒ์ด๊ณ ,
์ด๋ ๋ด๊ฐ ๊ฐ๊ณ ์๋ Disposable์ ๋ฐฐ์ด์ ์ํํ๋ฉฐ dispose() ๋ฉ์๋๋ฅผ ํธ์ถํด์ฃผ๊ธฐ ๋๋ฌธ์,
๋ฑํ ๋ณ๋์ ์์
์ ํด์ฃผ์ง ์์๋,
DisposeBag์ด ๊ฐ๊ณ ์๋ Disposable๋ค์ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋์ง ์์ ์ ์๋ ๊ฒ์ด๋ค.
โ ๋ง์ฝ ํน์ ์๊ฐ์ disposeBag์ ์ง์ ๋น์์ฃผ์ด์ผ ํ๋ ์ํฉ์ด๋ผ๋ฉด ํด๋น ์ํฉ์ ๋ง๊ฒ disposeBag์ ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ํด์ฃผ๋ฉด ๋๋ค.
disposeBag์ ์์ ์ด deinit ๋ ๋,
์์ ์ด ๊ฐ์ง๊ณ ์๋ ํด๋น Disposable์ ๋ชจ๋ dispose() ์์ผ๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์,
class HjViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let disposeBag: DisposeBag = .init()
Observable<Int>.interval(
.seconds(1)
scheduler: MainScheduler.instance
)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}
}
Swift
๋ณต์ฌ
์์ฒ๋ผ, ๋ง์ฝ disposeBag์ ์ ์ญ์ด ์๋ viewDidLoad ๋ฉ์๋ ์์ ์ง์ญ๋ณ์๋ก ์ ์ธํ ๊ฒฝ์ฐ,
viewDidLoad ๋ฉ์๋๊ฐ ๋๋ ๋ disposeBag๋ ๊ฐ์ด ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋๊ธฐ ๋๋ฌธ์,
์ด๋ ๋ฑ๋ก ๋์ด ์๋ Observable ๋ํ ๋ค ๊ฐ์ด ๊ตฌ๋
ํด์ ๋์ด ๋ฒ๋ ค ์ ์ฝ๋์์์ Observable์ด ์๋ํ์ง ์์ ๊ฒ์ด๋ค.
๋ฐ๋ผ์, disposeBag์ ์ฌ์ฉํ ๋๋ ์ ์ํด์ผํ๋ค โ ๋ณดํต ์ ์ญ์ ๋๋ค.
ย Subscribe ํ ๋ ๋ง์ง๋ง ํด๋ก์ onDisposed:
public func subscribe(
onNext: ((Element) -> Void)? = nil,
onError: ((Swift.Error) -> Void)? = nil,
onCompleted: (() -> Void)? = nil,
onDisposed: (() -> Void)? = nil
) -> Disposable
Swift
๋ณต์ฌ
ํด๋น Observable์ด ๊ตฌ๋
ํด์ ๋ ๋ใ
ก
์ฆ, disposed() ๋ฉ์๋๊ฐ ๋ถ๋ฆด ๋ ์คํ ๋ ํด๋ก์ ๋ฅผ ๋๊ฒจ์ฃผ๋ ๊ฒ์ด๋ค.
โ onError๋ onComplete์ผ๋ก ์ธํด disposed ๋ ๋๋ ๋น์ฐํ ์คํ๋๋ค.
โ ํด๋น Observable์ด ๊ตฌ๋
ํด์ ๋ ๋, ๋ฑ๋กํด๋์ ํด๋ก์ ๊ฐ ์คํ๋๋ค.