如果我们有种情况:
- 将网络请求结果包装在一个序列里面
- 多次订阅的话,也只是进行一次网络请求
- 网络请求的返回结果要在主线程
如果我们常规写法,就如下:
let result1 = inputTF.rx.text.skip(1)
.flatMap { [weak self](input) -> Observable<Any> in
return (self?.dealwithData(inputText: input ?? ""))!
.observeOn(MainScheduler.instance) // 网络请求结果,返回到主线程
.catchErrorJustReturn("检测到了错误事件") // 返回业务错误
}
.share(replay: 1, scope: .whileConnected) // 共享状态,有多个订阅的时候,只是走了一次网络请求
result1.subscribe(onNext: { (elemt) in
print("订阅到:\(elemt)")
})
result1.subscribe(onNext: { (elemt) in
print("订阅到:\(elemt) - \(Thread.current)")
})
// 将网络请求结果包装成序列
// 因为返回的有String和NSError两种,所以 Observable<Any> 中用Any
// 如果没有NSError,就可以写成 Observable<Any>
func dealwithData(inputText:String)-> Observable<Any>{
print("请求网络了 \(Thread.current)") // data
return Observable<Any>.create({ (ob) -> Disposable in
if inputText == "1234" {
ob.onError(NSError.init(domain: "com.lgcooci.cn", code: 10086, userInfo: nil)) // 网络返回的错误信息
}
DispatchQueue.global().async { // 异步发送信号
print("发送之前看看: \(Thread.current)")
ob.onNext("已经输入:\(inputText)")
ob.onCompleted()
}
return Disposables.create()
})
}
/*
输出:
请求网络了 <NSThread: 0x600001bcd440>{number = 1, name = main}
发送之前看看: <NSThread: 0x600001b50540>{number = 4, name = (null)}
订阅到:已经输入:1
订阅到:已经输入:1 - <NSThread: 0x600001bcd440>{number = 1, name = main}
*/
上面代码如果注释掉.observeOn(MainScheduler.instance)
和.share(replay: 1, scope: .whileConnected)
那么订阅几次就会进行几次网络请求,并且网络请求返回的结果也没有在主线程。
但是这样写还是有点繁琐,所以这时就要用到老司机Driver登场实现,他主要是对上面的代码进行了封装,当然网络请求的方法不变。代码如下:
// driver一般可用于请求网络然后绑定到UI
let result = inputTF.rx.text.orEmpty
.asDriver() // inputTF.rx.text序列转为driver类型,前后类型相同才能进行flatMap函数
.flatMap {
return self.dealwithData(inputText: $0)
.asDriver(onErrorJustReturn:"检测到了错误事件") // 将网络请求结果转为driver类型 // “检测到了错误事件” 是业务上我们自己封装的错误信息
}
result.map{ "长度:\(($0 as! String).count)"} // 将Any类型转换为String类型
.drive(self.textLabel.rx.text) // 将结果绑定到textLabel的text上
.disposed(by: disposeBag)
result.map{ "\($0 as! String)" } // 将Any类型转换为String类型
.drive(self.btn.rx.title()) // 将结果绑定到btn的title
.disposed(by: disposeBag)
分析
继续源代码分析:
extension ControlProperty { // UI事件封装
/// Converts `ControlProperty` to `Driver` trait.
///
/// `ControlProperty` already can't fail, so no special case needs to be handled.
public func asDriver() -> Driver<Element> {
return self.asDriver { _ -> Driver<Element> in
#if DEBUG
rxFatalError("Somehow driver received error from a source that shouldn't fail.")
#else
return Driver.empty()
#endif
}
}
}
这里我们会发现这里是将ControlProperty
转为Driver
类型,又由上一篇文章知道ControlProperty
里是有控件的序列和观察者的,可以看作是对控件的封装。
再看所有提供asDriver()
方法的地方,即所有可以转化为Driver
的地方:
可见只有这三个类有
Driver
的扩展,即转化为Driver
的方法,所以Driver
一般都是和控件UI绑定。
Driver的定义
虽然叫老司机Driver
,但是它其实本质并不是老司机,而只是取的一个别名,来到它定义的源代码:
// 这上面的DriverSharingStrategy参数就是下面的结构体
// 并且定义的Driver只需传后面的那个参数Element。DriverSharingStrategy这个参数不传,默认就是它
public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
// 结构体 DriverSharingStrategy
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { return SharingScheduler.make() // make方法点进去,默认在主线程}
public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
return source.share(replay: 1, scope: .whileConnected) // 共享状态,有多个订阅的时候,只是走了一次网络请求
}
}
extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy {
/// Adds `asDriver` to `SharingSequence` with `DriverSharingStrategy`.
public func asDriver() -> Driver<Element> {
return self.asSharedSequence()
}
}
所以Driver
的本质是序列SharedSequence
,再来看SharedSequence
:
public struct SharedSequence<SharingStrategy: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType {
let _source: Observable<Element>
init(_ source: Observable<Element>) {
self._source = SharingStrategy.share(source) // SharingStrategy 进不去,因为关联的是 SharingStrategyProtocol 类型
/*
public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
因为前面给Driver取别名,参数名字叫DriverSharingStrategy,所以当Driver初始化的时候,传进来的SharingStrategyn默认是DriverSharingStrategy
*/
}
这里的SharingStrategy
因为没有传,所以默认就是前面的结构体DriverSharingStrategy
,share
方法也就是前面结构体中的share
方法。并且这里的source
,也即是被当做Element
,从下面这段代码初始化Driver
传过来的:
public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<Element>) -> Driver<Element> {
let source = self
.asObservable()
.observeOn(DriverSharingStrategy.scheduler)
.catchError { error in
onErrorRecover(error).asObservable() // 返回错误事件的序列
}
return Driver(source)
//这里的source相当于Element,只是传了后面这个参数,前面这个DriverSharingStrategy没有传,所以就是默认值DriverSharingStrategy
// return Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
}
总结
参考文档注释:
Trait that represents observable sequence with following properties:
- it never fails
- it delivers events on `MainScheduler.instance`
- `share(replay: 1, scope: .whileConnected)` sharing strategy
1、Driver 可以说是最复杂的 trait,它的目标是提供一种简便的方式在 UI 层编写响应式代码。
2、如果我们的序列列满⾜足如下特征,就可以使⽤用它:
• 不会产⽣生 error 事件,当有error的时候,可以返回自己业务逻辑的相关错误提示
• 一定在主线程监听(MainScheduler)
• 共享状态变化(shareReplayLatestWhileConnected)
为什什么要使⽤用 Driver?
(1) Driver 最常使用的场景应该就是需要用序列来驱动应用程序的情况了,⽐比如:
• 通过 CoreData 模型驱动 UI
• 使⽤用⼀一个 UI 元素值(绑定)来驱动另⼀一个 UI 元素值
(2) 与普通的操作系统驱动程序⼀一样,如果出现序列列错误,应 ⽤用程序将停⽌止响应⽤用户输⼊入。
(3) 在主线程上观察到这些元素也是极其重要的,因为 UI 元素和应⽤用程序逻辑通常不不是线程安全的,因为更新UI必须在主线程。
(4) 此外,使⽤用构建 Driver 的可观察的序列列,它是共享状态变化,可以多次订阅,只用走一次网络请求。
网友评论