3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【RxSwift】Reactive の Extension を作成する際にメモリリークが発生した

Last updated at Posted at 2021-01-25

環境

  • Xcode 12.3
  • RxSwift 5.1.1

目的

  • TabTestViewControllerself.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)
    }
}
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?