RxSwift学习之 Driver;Subject

·  阅读 595
RxSwift学习之 Driver;Subject

Driver

案例引入

请求一次网络,绑定到UI上

采用Observerable

let result0  = 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)
// share(replay: 1, scope: .whileConnected) 共享状态 确保result被订阅多次时,网络请求只执行一次,不重复执行

// 多次订阅
result0.subscribe(onNext: { (element) in
        print("第一次订阅到result0:\(element)")
    }).disposed(by: disposeBag)

result0.subscribe(onNext: { (element) in
        print("第二次订阅到result0:\(element) - \(Thread.current)")
    }).disposed(by: disposeBag)
复制代码

采用Driver

// 请求一次网络
// 绑定到了UI - 主线程
// titlt - 非error
let result  = inputTF.rx.text.orEmpty
    .asDriver()
    .flatMap {
        return self.dealwithData(inputText: $0)
            .asDriver(onErrorJustReturn: "检测到了错误事件")
    }

result.map { "长度: \(($0 as! String).count)"}
    .drive(self.textLabel.rx.text)
    .disposed(by: disposeBag)

result.map { "\($0 as! String)"}
    .drive(self.btn.rx.title())
    .disposed(by: disposeBag)
复制代码
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.pxwx.www", code: 10086, userInfo: nil))
        }
        DispatchQueue.global().async {
            print("发送之前看看: \(Thread.current)")
            ob.onNext("已经输入:\(inputText)")
            ob.onCompleted()
        }
        return Disposables.create()
    })
}
复制代码

老司机是什么

1、Driver 可以说是最复杂的 trait,它的⽬目标是提供一种简便的⽅式在 UI 层编写响应式代码。
2、如果我们的序列列满⾜足如下特征,就可以使⽤用它:

  • 不会产⽣生 error 事件
  • 一定在主线程监听(MainScheduler)
  • 共享状态变化(shareReplayLatestWhileConnected)

为什要使用 Driver?

1、Driver 最常使⽤用的场景,应该就是需要⽤序列来驱动应⽤程序的情况了,比如:

  • 通过 CoreData 模型驱动 UI
  • 使⽤用一个 UI 元素值(绑定)来驱动另一个 UI 元素值

2、与普通的操作系统驱动程序一样,如果出现序列列错误,应用程序将停⽌响应用户输入。
3、在主线程上观察到这些元素也是极其重要的,因为 UI 元素和应⽤程序逻辑通常不是线程安全的。
4、此外,使⽤用构建 Driver 的可观察的序列列,它是共享状态变化的。

基本源码解析

Driver其实就是一个序列的别名,为了方便记忆。

public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
复制代码

image.png

image.png

image.png

image.png

OK!我们看到了source.share(replay: 1, scope: .whileConnected),这样就明白了Driver其实也就是对序列的进一层的封装。

Subject

来看一个非常特殊的类型-Subject,为什么说它特殊呢?原因很简单:Subject既可以做序列,也可以做观察者 正是因为这一特性,所以在实际开发中被大量运用。下面我们一起来解读一下这个特殊的Subject

基本原理

首先我们来看看:SubjectType的原理

public protocol SubjectType : ObservableType {
      // 关联了观察者类型,具备这个类型的能力
    associatedtype SubjectObserverType : ObserverType
    func asObserver() -> SubjectObserverType
}
复制代码
  • SubjectType首先就是继承了ObservableType,具有序列特性
  • 关联了观察者类型,具备这个类型的能力
  • 下面我们通过一个具体类型来感受一下`subject

image.png

  • 很明显能够订阅信号(序列最基本的能力)
  • 能够发送响应,又是观察者的能力
  • 查看底层源码分析

订阅响应流程

image.png

  • self._observers.insert(observer.on): 通过一个集合添加进去所有的订阅事件,很明显在合适的地方一次性全部执行
  • 其中也返回这次订阅的销毁者,方便执行善后工作: synchronizedUnsubscribe->self._observers.removeKey(disposeKey)

image.png

  • 便利通过key获取响应bag中的value,执行集合移除
  • 因为没有相应持有关系,达到自动释放销毁

发送信号流程

image.png

  • 这个地方估计大家看起来麻烦恶心一点,但是你用心看不难体会
  • 这里主要调用了dispatch函数,传了两个参数:self._synchronized_on(event)event
  • 查看dispatch函数源码

image.png

  • bag._value0?(event)首先执行事件的回调
  • 判断bag._onlyFastPath的情况,默认会开启快速通道!
  • 如果是开启慢速通道,需要从刚刚添加进bag包裹里面的匹配对挨个进行pairs[i].value(event),外界事件回调,然后拿回外界封装的闭包的闭包调用:element(event)

image.png

  • 这里如果self._isDisposed || self._stopped成立就会返回一个空的集合,也就没有序列的响应
  • .completed, .error都会改变状态self._stopped = true,也就是说序列完成或者错误之后都无法再次响应了
  • .completed, .error还会移除添加在集合里面的内容

其实如果你对前面序列的流程掌握了,这个subject的流程也不再话下,只是subject 把订阅流程和响应流程都内部实现,所以也就没有必要引入sink image.png

各种Subject

PublishSubject

可以不需要初始来进行初始化(也就是可以为空),并且它只会向订阅者发送在订阅之后才接收到的元素。

// PublishSubject
// 1:初始化序列
let publishSub = PublishSubject<Int>() //初始化一个PublishSubject 装着Int类型的序列
// 2:发送响应序列
publishSub.onNext(1)
// 3:订阅序列
publishSub.subscribe { print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送响应
publishSub.onNext(2)
publishSub.onNext(3)
/*
订阅到了: next(2)
订阅到了: next(3)
*/
复制代码
  • 信号:1是无法被订阅的,只接受订阅之后的响应

image.png

BehaviorSubject

通过一个默认初始值来创建,当订阅者订阅BehaviorSubject时,会收到订阅后Subject上一个发出的Event,如果还没有收到任何数据,会发出一个默认值。之后就和PublishSubject一样,正常接收新的事件。

PublishSubject 稍微不同就是BehaviorSubject这个家伙有个存储功能:存储上一次的信号

print("**********BehaviorSubject**********")
// BehaviorSubject
// 1:创建序列
let behaviorSub = BehaviorSubject.init(value: 100)
// 2:发送信号
behaviorSub.onNext(2)
behaviorSub.onNext(3)
// 3:订阅序列
behaviorSub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送
behaviorSub.onNext(4)
behaviorSub.onNext(5)
// 再次订阅
behaviorSub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
/*
**********BehaviorSubject**********
订阅到了: next(3)
订阅到了: next(4)
订阅到了: next(5)
订阅到了: next(5)
*/
复制代码
  • 当没有信号的时候,会默认发送 信号:100
  • 只能储存一个信号:信号2 会被 信号3 覆盖
  • 订阅信号之前能够储存信号

image.png

  • 初始化的时候带有一个属性保存一个信号
  • 事件响应:新事件会覆盖原来的事件
  • 其他流程和publish一样

image.png

ReplaySubject

ReplaySubject 发送源Observable 的所有事件无论observer什么时候开始订阅。

print("**********ReplaySubject**********")
// ReplaySubject
// 1:创建序列
let replaySub = ReplaySubject<Int>.create(bufferSize: 2)
// let replaySub = ReplaySubject<Int>.createUnbounded()

// 2:发送信号
replaySub.onNext(1)
replaySub.onNext(2)
replaySub.onNext(3)
replaySub.onNext(4)

// 3:订阅序列
replaySub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送
replaySub.onNext(7)
replaySub.onNext(8)
replaySub.onNext(9)

/*
**********ReplaySubject**********
订阅到了: next(3)
订阅到了: next(4)
订阅到了: next(7)
订阅到了: next(8)
订阅到了: next(9)
 */
复制代码
  • 一个bufferSize空间,想存储多少次响应就是多少次
  • 调用 addValueToBuffer 函数将发送的元素加入到 queue 中。
  • 调用 trim 函数删除 queue 中大于 bufferSize 的多余的元素。
  • 其他流程照旧
  • 源码里面就是相对于BehaviorSubject的储存属性变成了集合

AsyncSubject

AsyncSubject只发送由源Observable发送的最后一个事件,并且只在源Observable完成之后。(如果源Observable没有发送任何值,AsyncSubject也不会发送任何值。)

print("**********AsyncSubject**********")
// AsyncSubject
// 1:创建序列
let asynSub = AsyncSubject<Int>.init()
// 2:发送信号
asynSub.onNext(1)
asynSub.onNext(2)
// 3:订阅序列
asynSub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送
asynSub.onNext(3)
asynSub.onNext(4)
asynSub.onError(NSError.init(domain: "pxwx", code: 10086, userInfo: nil))
asynSub.onCompleted()
/*
**********AsyncSubject**********
订阅到了: error(Error Domain=pxwx Code=10086 "(null)")
*/
复制代码

image.png

Variable

Variable废弃了,这里贴出代码以供大家遇到老版本! 由于这个Variable的灵活性所以在开发里面应用非常之多!

print("**********Variable**********")
// Variable : 5.0已经废弃(BehaviorSubject 替换) - 这里板书 大家可以了解一下
// 1:创建序列
let variableSub = Variable.init(1)
// 2:发送信号
variableSub.value = 100
variableSub.value = 10
// 3:订阅信号
variableSub.asObservable().subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送
variableSub.value = 1000
/*
**********Variable**********
订阅到了: next(10)
订阅到了: next(1000)
*/
复制代码

BehaviorRelay

  • 替换原来的Variable
  • 可以储存一个信号
  • 随时订阅响应
  • 响应发送的时候要注意:behaviorR.accept(20)
print("**********BehaviorRelay**********")
let behaviorRelay = BehaviorRelay(value: 100)
behaviorRelay.subscribe(onNext: { (num) in
    print("订阅到了:\(num)")
}).disposed(by: disposbag)
behaviorRelay.accept(1000)
/*
**********BehaviorRelay**********
订阅到了:100
订阅到了:1000
订阅到了: completed
*/
复制代码

分享:Swift资料下载

收录自|地址

分类:
iOS
标签:
分类:
iOS
标签: