RxSwift 数据绑定


BindTo

官方摘录:Creates new subscription and sends elements to observer.
     func rxSwiftBindTo() {
        let mobile = Variable<String>(self.getExtra("phone") as! String)

        mobile.asObservable()
            .bind(to: rxViewModel.mobile)
            .disposed(by: dispose)

        bgView.msgVerifyCodeTextField.rx.text.orEmpty
            .bind(to: rxViewModel.mobileCode)
            .disposed(by: dispose)

        bgView.loginPwdTextField.rx.text.orEmpty
            .bind(to: rxViewModel.loginPwd)
            .disposed(by: dispose)

        bgView.regBtn.rx.tap
            .bind(to: rxViewModel.registerTaps)
            .disposed(by: dispose)
     }

Driver

官方摘录:Subscribes an element handler, a completion handler and disposed handler to an observable sequence.

This method can be only called from \`MainThread\`.

Error callback is not exposed because \`Driver\` can't error out.
    func driverTo() {
        let passwordViewModel = InputPasswordViewModel(passwordTextField.rx.text.orEmpty.asDriver())

        passwordViewModel.loginButtonEnabled.drive(onNext: { [unowned self] valid in
            self.loginBtn.isEnabled = valid
            self.loginBtn.isUserInteractionEnabled = valid
            self.loginBtn.backgroundColor =  valid ? UIColor(valueRGB: 0xc8a66b) :  UIColor(valueRGB: 0xc8a66b, alpha: 0.3)
        })
            .disposed(by: disposeBag)
    }
Driver是RxSwift精心制作的,专门提供给UI层的一个接口。Driver序列不允许发出error,监听只会在主线程中。

Practical usage example

let results = query.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
    }

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

这段代码的主要目的是:

  • 取出用户输入稳定后的内容
  • 向服务器请求一组结果
  • 将返回的结果绑定到两个 UI 元素上:tableView和 显示结果数量的label

那么这里存在什么问题?

  • 如果fetchAutoCompleteItems的序列产生了一个错误(网络请求失败),这个错误将取消所有绑定,当用户输入一个新的关键字时,是无法发起新的网络请求。
  • 如果fetchAutoCompleteItems在后台返回序列,那么刷新页面也会在后台进行,这样就会出现异常崩溃。
  • 返回的结果被绑定到两个 UI 元素上。那就意味着,每次用户输入一个新的关键字时,就会分别为两个 UI 元素发起 HTTP 请求,这并不是我们想要的结果。

一个更好的方案是这样的:

let results = query.rx.text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // 结果在主线程返回
            .catchErrorJustReturn([])           // 错误被处理了,这样至少不会终止整个序列
    }
    .share(replay: 1)                             // HTTP 请求是被共享的

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) {
      (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

以下是使用Driver优化后的代码:

let results = query.rx.text.asDriver()        // 将普通序列转换为 Driver
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .asDriver(onErrorJustReturn: [])  // 仅仅提供发生错误时的备选返回值
    }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx.text)               // 这里改用 `drive` 而不是 `bindTo`
    .disposed(by: disposeBag)                 // 这样可以确保必备条件都已经满足了

results
    .drive(resultsTableView.rx.items(cellIdentifier: "Cell")) {
      (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

首先第一个asDriver方法将ControlProperty转换为Driver

然后第二个变化是:

.asDriver(onErrorJustReturn: [])
以下情况你可以使用Driver替换BindTo:
  • 不能发出error

  • 在主线程中监听

  • 共享事件流 (shareReplayLatestWhileConnected)


KVO

KVO是一个Objective-C机制。 这意味着它没有考虑到安全类型。 这个项目试图解决一些问题。

为什么说KVO是非类型安全的,参考《关于KVO的那些事 之 KVO安全用法封装》

view
  .rx.observe(CGRect.self, "frame")
  .subscribe(onNext: { frame in
    ...
  })

or

view
  .rx.observeWeakly(CGRect.self, "frame")
  .subscribe(onNext: { frame in
    ...
  })

rx.observe

rx.observeis more performant because it's just a simple wrapper around KVO mechanism, but it has more limited usage scenarios

  • it can be used to observe paths starting fromselfor from ancestors in ownership graph (retainSelf = false)
  • it can be used to observe paths starting from descendants in ownership graph (retainSelf = true)
  • the paths have to consist only ofstrongproperties, otherwise you are risking crashing the system by not unregistering KVO observer before dealloc.

E.g.

self.rx.observe(CGRect.self, "view.frame", retainSelf: false)

rx.observeWeakly

rx.observeWeaklyhas somewhat slower thanrx.observebecause it has to handle object deallocation in case of weak references.

It can be used in all cases whererx.observecan be used and additionally

  • because it won't retain observed target, it can be used to observe arbitrary object graph whose ownership relation is unknown
  • it can be used to observeweakproperties

E.g.

someSuspiciousViewController.rx.observeWeakly(Bool.self, "behavingOk")

观察结构

RxCocoa has built in support for KVO observing ofCGRect,CGSizeandCGPointstructs.

翻译自:RxSwift官网

results matching ""

    No results matching ""