RxSwiftCommunity / RxSwiftExt

A collection of Rx operators & tools not found in the core RxSwift distribution
MIT License
1.32k stars 213 forks source link

Add withUnretained to SharedSequence #243

Closed freak4pc closed 3 years ago

freak4pc commented 4 years ago

Based on the request in #242. IRT other traits - there's no real value to add it to Completable... we could add to Maybe and Single in a separate PR.

This PR has a single failing test, I can't really put my finger on what's going on there (the same test passes for Signal but fails for Driver).

If anyone can take a poke at it, that'd be great :)

Thanks !

getogrand commented 4 years ago

I found a bug on this implementation.

In below scenario, the viewModel.estimatedShippingDate is not disposed, so that the viewController and viewModel were not removed in the memory. I don't really understand why this causes retain cycle nevertheless the self is not referred in the closure body.

The retain cycle is not caused if I change .withUnretained(self) with normal [weak self] capture list.

enum YGShippingType: String, Codable, Comparable, CustomStringConvertible {
  // ...
}

class SomeViewModel {
  private let _estimatedShippingDate = PublishRelay<(Date, YGShippingType)?>()

  // ...

  var estimatedShippingDate: Driver<(Date, YGShippingType)?> {
    _estimatedShippingDate.asDriver(onErrorJustReturn: nil)
      .distinctUntilChanged({
        $0?.0 == $1?.0 && $0?.1 == $1?.1
      })
  }
}

class SomeViewController: UIViewController {
  private let disposeBag = DisposeBag()
  var viewModel: ViewModel! // Injected by swinject

  // ...

  override func viewDidLoad() {
    super.viewDidLoad()
    bindCartTableHeaderView()
  }

  private func bindCartTableHeaderView() {
    // SharedSequence.withUnretained() causes retain cycle
    viewModel.estimatedShippingDate // Driver<(Date, YGShippingType)?>
      .map({ $0 == nil })
      .distinctUntilChanged()
      .withUnretained(self)
      .drive(onNext: { (self, hide) in
        // Note that we did not refer `self`
        print("blabla")
      })
      .disposed(by: disposeBag)

    /* 
    // ObservableType.withUnretained() doesn't cause retain cycle
    viewModel.estimatedShippingDate
      .map({ $0 == nil })
      .distinctUntilChanged()
      .asObservable()
      .withUnretained(self)
      .subscribe(onNext: { (self, hide) in
        print("blabla")
      })
      .disposed(by: disposeBag)
    */
  }
}