RxSwift中的老司机Driver

2019-08-03  本文已影响0人  简_爱SimpleLove

如果我们有种情况:

如果我们常规写法,就如下:

        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的方法,所以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因为没有传,所以默认就是前面的结构体DriverSharingStrategyshare方法也就是前面结构体中的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 的可观察的序列列,它是共享状态变化,可以多次订阅,只用走一次网络请求。

上一篇下一篇

猜你喜欢

热点阅读