美文网首页iOS开发框架使用与解析
RxSwift - 老司机Driver使用与浅析

RxSwift - 老司机Driver使用与浅析

作者: Lcr111 | 来源:发表于2023-03-06 21:49 被阅读0次
    前言

    获取网络数据展示在UI界面上,可以说App上的过半需求都是这样的,实现这个过程方式有很多种,今天我们就借这个功能来讲讲Driver的使用,看看Driver带来的哪些优点。

    I'm a Driver
    1、功能代码
    func dealWithData(inputText: String)-> Observable<Any> {
            print("请求网络:\(Thread.current)")
            return Observable<Any>.create({ ob -> Disposable in
                if inputText == "1234" {
                    ob.onError(NSError.init(domain: "LcrError", code: 10085, userInfo: nil))
                }
                DispatchQueue.global().async {
                    print("发送之前:\(Thread.current)")
                    ob.onNext("已经输入:\(inputText)")
                    ob.onCompleted()
                }
                return Disposables.create()
            })
        }
    
    示例图

    为了实现当输入改变时后,多个地方同时请求同一个接口数据的过程,即同时订阅多次(此处请求两次),利用RxSwift框架的三种方式完成。

    2、常规方案
    let result = inputTF.rx.text.skip(1)
          //序列中的序列
          .flatMap { input in
                    return self.dealWithData(inputText: input!)
          }
    _ = result.subscribe({ element in
           print("订阅到了\(element)")
    })
    _ = result.subscribe({ element in
           print("订阅到了\(element) - \(Thread.current)")
           //更新UI
    })
    

    flatMap:此操作符会对源Observable的每一个元素应用一个转换方法,将它们转换成Observables。然后将这些Observables的元素合并后再发送出来,即又将其合成一个Observable序列。比如当Observable的元素本身拥有其他的Observable时,我们可以将所有子Observables的元素发送出来。
    如上代码中,当进行两次订阅时,内部序列会按先后顺序将信号返回出来。
    此时的打印情况如下:

    正常结果
    抛出错误

    可见的确是分别进行了两次网络请求,并且两次都有返回,网络请求在主线程,发送之前是在子线程发出去的,然后打印结果也是在子线程,那么我们就能发现一下几个问题:

    • 当订阅几次时,会进行几次网络请求,但数据都是一样的,会造成网络资源的浪费。
    • 如果我们在最后返回时候如果要进行UI刷新的话,在子线程就会报错甚至崩溃。
    • 错误事件的处理,当输入1234时,会返回错误,后续订阅就失效,订阅就断开,再输入其他都没用了。这肯定不行的。
    3、优化后方案
    let result = inputTF.rx.text.skip(1)
            //序列中的序列
                .flatMap { input in
                    return self.dealWithData(inputText: input!)
                        .observe(on: MainScheduler())
                        .catchAndReturn("监测到错误事件")//error起死回生
                }
                .share(replay: 1, scope: .whileConnected)
    
            _ = result.subscribe({ element in
                print("订阅到了\(element)")
            })
            _ = result.subscribe({ element in
                print("订阅到了\(element) - \(Thread.current)")
                //更新UI
            })
    

    打印结果如下:


    方案二正常结果
    方案二返回错误

    请求网络只有一次,订阅后收到信号是在主线程,错误信号返回后,订阅不会中断,还可以继续运行。
    添加了三句代码,增加三个操作,解决以上三个问题。

    • .share(replay: 1, scope: .whileConnected) 控制请求只有一次,达到共享网络数据目的。
    • .observe(on: MainScheduler()) 控制信号返回后是在主线程,这样刷新UI等操作不会报错。当然你也可在返回时候调用DispatchQueue.main.async{},但相对而言.observe(on方式更符合RxSwift用法。
    • .catchAndReturn("xxxxxxx ") 处理error信号,让error信号起死回生。
    4、最佳推荐方案
    let result = inputTF.rx.text
                .asDriver()
                .flatMap {
                    return self.dealWithData(inputText: $0!)
                        .asDriver(onErrorJustReturn: "监测到错误事件")
                }
            _ = result.map { "长度:\(($0 as! String).count)" }
                .drive(self.textLabel.rx.text)
    
            _ = result.map { "\($0)" }
                .drive(self.btn.rx.title())
    

    本方案采用Driver方式,asDriver()将源序列转化为一个SharedSequence类型的结构体实例,通过.drive方法来订阅信号(类似bind(to:)),就很完美滴解决了上面三个问题。

    请求网络:<_NSMainThread: 0x60000399c040>{number = 1, name = main}
    发送之前:<NSThread: 0x6000039a1400>{number = 5, name = (null)}

    接下来就看看Driver到底是什么?到底做了什么?
    首先看看asDriver():

    asDriver
    self.asDriver
    注意此处生成的source即外界序列本身(Observable->Driver->Observable),并留意此处的DriverSharingStrategy类型的结构体,后面肯定是需要用到这个结构体的。查看DriverSharingStrategy.scheduler,
    public static var scheduler: SchedulerType { SharingScheduler.make() }
    public private(set) static var make: () -> SchedulerType = { MainScheduler() }
    

    看到MainScheduler,那是不是当前序列就是在这里设置运行的线程的呢?

    接着查看Driver的初始化:


    driver初始化

    asDriver()将序列转换为Driver类型,所以flatMap里面需要返回一个Driver类型的,所以使用到了.asDriver(将加载数据但会结果转换为Driver类型。

    接着我们来看看订阅方法.drive():

    .drive()
    这里面对当前任务是不是在主线程中运行进行了校验,着重看下self.asSharedSequence()方法:
    asSharedSequence
    发现有两个地方,我们先看第一个,发现是SharedSequenceConvertibleType协议里面的方法声明,且协议里有SharingStrategy关联类型:
    SharedSequenceConvertibleType
    返回查看第二个asSharedSequence使用的地方:
    asSharedSequence2
    发现是在刚刚Driver初始化的结构体里面,即将Observable转换为Driver类型。
    并注意此处序列的.subscribe(),所以在.drive里面有订阅的相关封装。
    到这里还没有看到SharingStrategy的真正类型是什么...
    全局搜索SharingStrategyProtocol协议,发现刚刚的DriverSharingStrategy遵循了此协议(有三处用到,我们直接来此查看)。且实现了share协议方法,断点调试的确来到这里:
    DriverSharingStrategy
    结合前面使用了此处的scheduler变量,我们可以确定SharingStrategy实际就是DriverSharingStrategy类型。
    方式二:也可使用lldb打印查看SharingStrategy的真正类型:
    SharingStrategy类型
    即SharingStrategy类型为DriverSharingStrategy。
    • DriverSharingStrategy.share方法里面正是.share(replay: 1, scope: .whileConnected),即解决第一个数据共享的问题的代码。
    • 那么引刃而解上方的scheduler即为解决第二个主线程UI刷新的问题。
    • .asDriver(onErrorJustReturn: "xxxxxx") 即解决了第三个错误处理的问题。

    所以在解决多个信号同时订阅一个信号的功能,Drvier提供了更方便的使用,规避了一些资源及错误处理等的问题。

    相关文章

      网友评论

        本文标题:RxSwift - 老司机Driver使用与浅析

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