環境
- Xcode 12.3
- RxSwift 5.1.1
目的
-
TabTestViewControllerのself.viewDidLayoutSubviews()呼び出しを検知しその際のself.indexを流すObservable<Int>を作成したい
TabTestViewController.swift
// こんな感じで利用する currentIndexDidChange を Extension で実装
self.rx.currentIndexDidChange
.subscribe(self.indexSubject)
.disposed(by: self.disposeBag)
当初実装
- 以下のように実装するとメモリリークが発生する
TabTestViewController+rx.swift
extension Reactive where Base: TabTestViewController {
var currentIndexDidChange: Observable<Int> {
// メモリリークが発生
return sentMessage(#selector(base.viewDidLayoutSubviews))
.map { _ in base.currentIndex }
.distinctUntilChanged()
.share(replay: 1)
}
}
原因
-
baseがクロージャ内部で強参照されていることによる
間違い: self を弱参照しようとした
- 「クロージャの中では
selfを弱参照」と手癖で実装しようとしたところコンパイルエラー-
Reactive<Base>は構造体のためweakが使えない
-
TabTestViewController+rx.swift
extension Reactive where Base: TabTestViewController {
var currentIndexDidChange: Observable<Int> {
// 'weak' may only be applied to class and class-bound protocol types, not 'Reactive<Base>'
return sentMessage(#selector(base.viewDidLayoutSubviews))
.map { [weak self] _ in
guard let self = self else { fatalError() }
return self.base.currentIndex
}
.distinctUntilChanged()
.share(replay: 1)
}
}
解決: base を弱参照とする
-
TabTestViewControllerはクラスのためweakで弱参照することができる
TabTestViewController+rx.swift
extension Reactive where Base: TabTestViewController {
var currentIndexDidChange: Observable<Int> {
// viewDidLayoutSubviewsのタイミングでcurrentIndexの変化を検知する
return sentMessage(#selector(base.viewDidLayoutSubviews))
.map { [weak base] _ in
guard let base = base else { fatalError() }
return base.currentIndex
}
.distinctUntilChanged()
.share(replay: 1)
}
}