環境
- 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)
}
}