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.observe
is 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 from
self
or 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 of
strong
properties, 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.observeWeakly
has somewhat slower thanrx.observe
because it has to handle object deallocation in case of weak references.
It can be used in all cases whererx.observe
can 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 observe
weak
properties
E.g.
someSuspiciousViewController.rx.observeWeakly(Bool.self, "behavingOk")
观察结构
RxCocoa has built in support for KVO observing ofCGRect
,CGSize
andCGPoint
structs.
翻译自:RxSwift官网