1.Subject
Subject 既攻也受,它既可以作为序列,又可以作为观察者。下面我们看一下既攻也守的原理:
public protocol SubjectType : ObservableType {/// The type of the observer that represents this subject.////// Usually this type is type of subject itself, but it doesn't have to be.associatedtype Observer: ObserverType/// Returns observer interface for subject.////// - returns: Observer interface for subject.func asObserver() -> Observer}
1)SubjectType
继承了ObservableType
,具有序列的特性
2)又通过associatedType关联了ObserverType,具有了观察者的特性
3)通过asObserver方法,可以直接从序列转化为观察者
1.最常用的Subject 1: BehaviorRelay
在5.0之前的版本最常用的是Variable,但是后来被废弃了,现在由BehaviorRelay代替,现在最常用的就是BehaviorRelay。BehaviorRelay的特性:
I. 可以存储一个信号(也就是我们常说的默认值)
II. 随时订阅响应(也就是BehaviorRelay的值一旦发生变化,就会立即被订阅到)
III. 注意:响应发送的时候使用的方法是accept方法: behaviorRelay.accept(300)
III.替换原来的Variable
示例代码:
func testBehaviorRelay() {let behaviorRelay = BehaviorRelay(value: 300)behaviorRelay.subscribe(onNext: { (num) inprint("订阅\(num)")}).disposed(by: disposeBag)print("打印:\(behaviorRelay.value)")behaviorRelay.accept(3000)}打印结果:
订阅300
打印300
订阅3000
注意:初始化值300也是可以订阅到的
2.最常用的Subject 2: PublishSubject
PublishSubject 可以不需要初始来进行初始化(也就是可以为空),并且它只会向订阅者发送在订阅之后才接收到的元素。
func testPublishSubject() {// 1:初始化序列let publishSub = PublishSubject<Int>() //初始化一个PublishSubject 装着Int类型的序列// 2:发送响应序列publishSub.onNext(1)// 3:订阅序列publishSub.subscribe {print("订阅到了:",$0)}.disposed(by: disposeBag)// 再次发送响应publishSub.onNext(2)publishSub.onNext(3)
}订阅到了: next(2)
订阅到了: next(3)
注意:信号1没有被订阅到,只接受订阅之后的响应
2.Driver
首先介绍两个概念,RXSwif的防抖与节流:比如在日常开发中, scroll、click、reload等高频率的触发事件,会过度损耗页面性能,导致页面卡顿,页面抖动。有时我们不希望在事件持续触发的过程中那么频繁地去执,此时防抖和节流是比较好的解决方案。
防抖:比如TextField一直输入时, 忽略 2 秒内的响应, 停止输入 2 秒后响应一次
userNameTextField.rx.text.debounce(RxTimeInterval.seconds(2), scheduler: MainScheduler.instance).subscribe(onNext: { str inlet date = Date()let format = DateFormatter()format.dateFormat = "HH:mm:ss"print("RX的debounce: \(format.string(from: date)) - \(str ?? "")")}).disposed(by: disposeBag)输出:
RX的debounce: 17:53:22 -
RX的debounce: 17:53:24 - 111
节流:比如TextField一直输入时, 每 2 秒触发一次
// 节流 (TextField一直输入时, 每 2 秒触发一次)userName.rx.text.throttle(RxTimeInterval.seconds(2), scheduler: MainScheduler.instance).subscribe(onNext: { str inlet date = Date()let format = DateFormatter()format.dateFormat = "HH:mm:ss"print("throttle: \(format.string(from: date)) - \(str ?? "")")}).disposed(by: disposeBag)输出:
throttle: 18:02:51 - Optional("")
throttle: 18:02:53 - Optional("1")
throttle: 18:02:55 - Optional("11111")
介绍Driver:
Driver
是一个精心准备的特征序列。它主要是为了简化 UI
层的代码,比如获取网络数据展示在UI界面上,今天我们就对这个功能来讲讲Driver
的使用。因为它具有以下Driver的特征:
I. 不会产生 error
事件
II. 一定在 MainScheduler
监听(主线程监听)
III. 会共享附加作用
1. 网络请求代码
func dealWithData(inputText: String)-> Observable<Any> {print("请求网络:\(Thread.current)")return Observable<Any>.create({ ob -> Disposable inif 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()})}输出:
请求网络:<_NSMainThread: 0x6000027705c0>{number = 1, name = main}
发送之前:<NSThread: 0x600002752600>{number = 5, name = (null)}
next(已经输入:123456)
completed
接下来再介绍RXSwift的三个高阶函数:
I. RXSwift的skip函数:从源可观察序列发出元素,直到跳过skip设置的个数再开始响应订阅。这个应用非常频繁 ,比如textField被点击但是还未输入文字的时候,不使用skip(1),就会被订阅到空字符串,这是不需要的,还有其他一些可观察序列的初始化过程是需要要订阅到的。
func testSkipMethod() {Observable.of(1, 2, 3, 4, 5, 6).skip(2).subscribe(onNext: { print($0) }).disposed(by: disposeBag)}输出:
2
3
4
5
6
II. RXSwift的map函数:map函数闭包应用于可观察序列发出的元素,并返回转换后的元素组成的新可观察序列。
let ob = Observable.of(1,2,3,4)ob.map { (number) -> Int inreturn number+2}.subscribe{print("\($0)")}.disposed(by: disposeBag)输出:
next(3)
next(4)
next(5)
next(6)
completed
III. RXSwift的flatMap函数:将可观察序列发射的元素转换为可观察序列,并将两个可观察序列的发射合并为一个可观察序列。比如此操作符会对源Observable的每一个元素应用一个转换方法,将它们转换成Observables。然后将这些Observables的元素合并后再发送出来,即又将其合成一个Observable序列。比如当Observable的元素本身拥有其他的Observable时,我们可以将所有子Observables的元素发送出来。
let result = userNameTextField.rx.text.skip(1).flatMap { input inreturn self.dealWithData(inputText: input!)}result.subscribe({ element inprint("订阅到了\(element)")}).disposed(by: disposeBag)result.subscribe({ element inprint("订阅到了\(element) - \(Thread.current)")//更新UI}).disposed(by: disposeBag)输出:
请求网络:<_NSMainThread: 0x6000030140c0>{number = 1, name = main}
请求网络:<_NSMainThread: 0x6000030140c0>{number = 1, name = main}
发送之前:<NSThread: 0x6000030b1300>{number = 10, name = (null)}
订阅到了next(已经输入:11)
发送之前:<NSThread: 0x6000030377c0>{number = 11, name = (null)}
订阅到了next(已经输入:11) - <NSThread: 0x6000030377c0>{number = 11, name = (null)}
由上面输出结果可以看出分别进行了两次网络请求,并且两次都有返回,网络请求在主线程,发送之前是在子线程发出去的,然后打印结果也是在子线程,那么我们就能发现如下几个问题:
I. 当订阅几次时,会进行几次网络请求,但数据都是一样的,会造成网络资源的浪费
II. 如果我们在最后返回时候如果要进行UI刷新的话,在子线程就会报错甚至崩溃
III. 错误事件的处理,当输入1234时,会返回错误,后续订阅就失效,订阅就断开,再输入其他都没用了。
2.进一步优化后的代码:
let result = userName.rx.text.skip(1).flatMap { input inreturn self.dealWithData(inputText: input!).observe(on: MainScheduler()).catchAndReturn("监测到错误事件")}.share(replay: 1, scope: .whileConnected)result.subscribe({ element inprint("订阅到了\(element)")}).disposed(by: disposeBag)result.subscribe({ element inprint("订阅到了\(element) - \(Thread.current)")//更新UI}).disposed(by: disposeBag)输出:
请求网络:<_NSMainThread: 0x600001ccc040>{number = 1, name = main}
发送之前:<NSThread: 0x600001c55780>{number = 8, name = (null)}
订阅到了next(已经输入:11)
订阅到了next(已经输入:11) - <_NSMainThread: 0x600001ccc040>{number = 1, name = main}
优化后的代码问题得到明显的解决,多个订阅只有一次网络请求,订阅后收到信号是在主线程,更新UI不会发生错误,错误信号返回后,订阅不会中断,还可以继续运行。
I. share(replay: 1, scope: .whileConnected)
控制多次订阅网络请求只有一次,达到共享网络数据目的
II. observe(on: MainScheduler())
控制信号返回是在主线程,这样更新UI等要求在主线程的操作不会发生错误
III. catchAndReturn("监测到错误事件")
处理error信号, 保证在遇到错误情况后不会停止订阅信号。
3.关于这个问题的最优解:Driver
let result = userName.rx.text.asDriver().flatMap {return self.dealWithData(inputText: $0!).asDriver(onErrorJustReturn: "监测到错误事件")}result.map { "输入字符串长度:\(($0 as! String).count)" }.drive(userNameTip.rx.text).disposed(by: disposeBag)result.map { "\($0)" }.drive(passwordTip.rx.text).disposed(by: disposeBag)
我们可以看到,使用Driver简洁高效的完成了上面的功能,接下来我们看一下Driver的底层原理
1)点击.asDriver(onErrorJustReturn: "监测到错误事件")进入底层源码
public func asDriver(onErrorJustReturn: Element) -> Driver<Element> {let source = self.asObservable().observe(on:DriverSharingStrategy.scheduler).catchAndReturn(onErrorJustReturn)return Driver(source)}
2)我们可以看到这段代码里实现了 catchAndReturn(onErrorJustReturn) 错误处理方法,再点击 .observe(on:DriverSharingStrategy.scheduler) 里的 DriverSharingStrategy.scheduler 进入源码
public struct DriverSharingStrategy: SharingStrategyProtocol {public static var scheduler: SchedulerType { SharingScheduler.make() }public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {source.share(replay: 1, scope: .whileConnected)}
}
3)再点击 SharingScheduler.make() 进入源码
public enum SharingScheduler {/// Default scheduler used in SharedSequence based traits.public private(set) static var make: () -> SchedulerType = { MainScheduler() }......
}
从上面我们可以看到,Driver订阅信号的操作是在 MainScheduler 主线程。
结语:
RXSwift在项目中经常使用的类和方法已经大体介绍完了,如果感觉有用的话,点个Star吧!