美文网首页
RxSwift中的老司机Driver

RxSwift中的老司机Driver

作者: 简_爱SimpleLove | 来源:发表于2019-08-03 01:00 被阅读0次

    如果我们有种情况:

    • 将网络请求结果包装在一个序列里面
    • 多次订阅的话,也只是进行一次网络请求
    • 网络请求的返回结果要在主线程

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

            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 的可观察的序列列,它是共享状态变化,可以多次订阅,只用走一次网络请求。

    相关文章

      网友评论

          本文标题:RxSwift中的老司机Driver

          本文链接:https://www.haomeiwen.com/subject/lrcddctx.html