代码之家  ›  专栏  ›  技术社区  ›  Tylerian

最新的怪异行为

  •  0
  • Tylerian  · 技术社区  · 6 年前

    今天我把我的代码从ReactiveSwift迁移到RxSwift,然后出现了这个奇怪的场景。

    我有一个 Observable withLatestFrom 运算符内部 ViewModel 类,并且它只在我在 视图模型 类,但不是在我在 ViewController

    这个 从最晚开始 可见的 从最晚开始

    // emit phrases when viewDidLoad emits
    let thePhrases = self.viewDidLoadSubject.withLatestFrom(self.configureWithPhrasesSubject)
    
    // This is the problematic Observable
    let printThePhrases = self.buttonTappedSubject.withLatestFrom(thePhrases)
    

    下面是我为展示这种奇怪行为而制作的代码,您可以在XCode中运行它并将调试器输出过滤器设置为 [!] 忽略模拟器生成的Garbage输出:

    import UIKit
    import RxSwift
    
    public final class RxTestViewModel {
        public init() {
            // emit configuredWithPhrases when viewDidLoad emits
            let configPhrases = self.viewDidLoadSubject
                .withLatestFrom(self.configureWithPhrasesSubject)
                .filter { $0 != nil }
                .map { $0! }
    
            // Show phrases to be printed on viewDidLoad
            self.toBePrinted = configPhrases.asObservable()
    
            _ = self.toBePrinted.subscribe(onNext: {
                print("[!][\(Thread.current)] -- ViewModel.toBePrinted.onNext -> \($0)")
            })
    
            // Print first phrase whenever buttonTapped() is called
            self.printSomething = self.buttonTappedSubject
                .withLatestFrom(self.configureWithPhrasesSubject
                .filter { $0 != nil }
                .map { $0! })
    
            _ = self.printSomething.subscribe(onNext: {
                print("[!][\(Thread.current)] -- ViewModel.printSomething.onNext -> \($0)")
            })
        }
    
        // MARK: Inputs
        private let configureWithPhrasesSubject = BehaviorSubject<[String]?>(value: nil)
        public func configureWith(phrases: [String]) {
            print("[!][\(Thread.current)] -- ViewModel.configureWith")
            self.configureWithPhrasesSubject.on(.next(phrases))
        }
    
        private let viewDidLoadSubject = PublishSubject<Void>()
        public func viewDidLoad() {
            print("[!][\(Thread.current)] -- ViewModel.viewDidLoad")
            self.viewDidLoadSubject.on(.next( () ))
        }
    
        private let buttonTappedSubject = PublishSubject<Void>()
        public func buttonTapped() {
            print("[!][\(Thread.current)] -- ViewModel.buttonTapped")
            self.buttonTappedSubject.on(.next( () ))
        }
    
        // MARK: Outputs
        public let printSomething: Observable<[String]>
        public let toBePrinted: Observable<[String]>
    }
    
    public final class RxTestViewController: UIViewController {
    
        private let button: UIButton = UIButton()
        private let viewModel: RxTestViewModel = RxTestViewModel()
    
        public static func instantiate() -> RxTestViewController {
            let vc = RxTestViewController()
                vc.viewModel.configureWith(phrases: ["First phrase", "Second phrase", "Third phrase"])
    
            return vc
        }
    }
    
    extension RxTestViewController {
        public override func viewDidLoad() {
            super.viewDidLoad()
    
            self.setupButton()
            self.setupViewModel()
    
            self.viewModel.viewDidLoad()
        }
    }
    
    extension RxTestViewController {
        private func setupViewModel() {
            _ = self.viewModel.toBePrinted
                .subscribeOn(ConcurrentMainScheduler.instance)
                .subscribe(onNext: {
                    print("[!][\(Thread.current)] -- RxTestViewController.toBePrinted.onNext -> \($0)")
                    self.viewModel.buttonTapped()
                })
    
            _ = self.viewModel.printSomething
                .subscribeOn(ConcurrentMainScheduler.instance)
                .subscribe(onNext: {
                    print("[!][\(Thread.current)] -- RxTestViewController.printSomething.onNext -> \($0)")
            })
        }
    }
    
    extension RxTestViewController {
        private func setupButton() {
            // Add to view
            self.view.addSubview(self.button)
    
            // Button config
            self.button.setTitle("CLICK ME", for: .normal)
            self.button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    
            // Auto-layout
            self.button.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                self.button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
                self.button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)])
        }
    
        @objc
        private func buttonTapped() {
            self.viewModel.buttonTapped()
        }
    }
    

    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.configureWith
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.viewDidLoad
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.toBePrinted.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- RxTestViewController.toBePrinted.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.buttonTapped
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.printSomething.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- RxTestViewController.printSomething.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    

    但我得到的却是:

    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.configureWith
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.viewDidLoad
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.toBePrinted.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- RxTestViewController.toBePrinted.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.buttonTapped
    [!][<NSThread: 0x600001fee280>{number = 1, name = main}] -- ViewModel.printSomething.onNext -> ["First phrase", "Second phrase", "Third phrase"]
    

    如您所见,观察者订阅不是在 ,只在

    有趣的是,如果我把观察者的 latestFrom buttonTapped() ),ViewModel订阅和ViewController订阅都将按预期调用。

    从最晚开始 来自的操作员 configPhrases 可观察链,并仅将其添加到 toBePrinted

    从最晚开始 对于已经应用了

    1 回复  |  直到 6 年前
        1
  •  0
  •   Daniel T.    6 年前

    我没有看到你所说的行为,可能是因为我展示的是视图控制器,而不是在测试工具中使用它?

    无论如何,当你这样做的时候要记住 a.withLatestFrom(b) a 在之前发出值 b 已发射任何值,则操作员将过滤掉发射。这是你的问题吗?