看完本系列前面几篇以后,估计你们也仍是有点懵逼,本系列前八篇也都是参考RxSwift
官方文档和一些概念作的解读。上几篇文章概念性的东西有点多,一时也是很难所有记住,你们脑子里面知道有这么个概念就行,用的时候,再来查阅一番,慢慢就掌握了。html
本篇主要来深刻了解一些RxSwift
实战中用到的一些重要知识点,这里面有不少本身的理解,因此难免会有一些错误的地方,还请你们多多交流,若有发现错误的地方,欢迎评论。swift
Rx
系列的核心就是Observable Sequence
这个相信你们心中已经有所了解了,这里再也不啰嗦了,建议你们看看前面几篇文章去了解一下。接下来介绍一些容易混淆和难以理解的概念。api
相信你们看前面几篇文章的时候,大量出现这两个东西,为了理解这两个东西,咱们先来简单介绍下观察者模式吧。网络
好比一个宝宝在睡觉,爸爸妈妈不可能时时刻刻待在那看着吧?那样子太累
了。他们该作啥作啥,只要听到宝宝哭声的时候,他们给宝宝喂奶就好了。这就是一个简单的观察者模式。宝宝是被观察者,爸爸妈妈是观察者也称做订阅者,只要被观察者发出了某一个事件,好比宝宝哭声,叫声都是一个事件,订阅者就会作出相应地响应。闭包
理解了观察者模式这两个概念就很好理解了,Observable
就是可被观察的,也就是咱们说的宝宝,他也是事件源。而Observer
就是咱们的观察者,也就是当收到事件的时候去作某些处理的爸爸妈妈。观察者须要去订阅(subscribe
)被观察者,才能收到Observable
的事件通知消息。函数
subscribe
是订阅sequence
发出的事件,好比next
事件,error
事件等。而subscribe(onNext:)
是监听sequence
发出的next
事件中的element
进行处理,他会忽略error
和completed
事件。相对应的还有subscribe(onError:)
和 subscribe(onCompleted:)
。fetch
当监听一个sequence
的时候,有消息事件来了,咱们作某些事情。可是这个sequence
再也不发送消息事件了,那么咱们的监听也就没有什么存在的价值了,因此咱们须要释放咱们这些监听资源,其实也就是内存资源释放。
释放某一个监听的时候,咱们有两种方式处理:spa
咱们能够手动调用释放方式,可是咱们通常不适用这种方式。线程
// 关于scheduler,咱们会在下面讲到 let subscription = Observable<Int>.interval(0.3, scheduler: SerialDispatchQueueScheduler.init(internalSerialQueueName: "scott")) .observeOn(MainScheduler.instance) //observeOn也会在下面讲到 .subscribe { event in print(event) } Thread.sleep(forTimeInterval: 2.0) subscription.dispose()
打印结果:code
next(0) next(1) next(2) next(3) next(4) next(5)
好比上面这个例子,咱们建立了一个subscription
监听,在两秒之后咱们不须要了,手动调用dispose()
方法,就能释放监听资源,再也不打印信息。上面的subscription
不管是在哪一个线程中监听,就算在主线程中调用的dispose()
方法同样会销毁资源。
除了上述手动释放资源外,还有一种自动方式,推荐你们使用这种方式,这种方式就像iOS
中的ARC
,会在适当的时候销毁观察者,自动释放资源。
let disposeBag = DisposeBag() Observable<Int>.empty() .subscribe { event in print(event) } .addDisposableTo(disposeBag)
如上个例子,咱们建立一个disposeBag
来盛放咱们须要管理的资源,而后把新建的监听都放进去,会在适当的时候销毁这些资源。若是你须要当即释放资源只须要新建一个DisposeBag()
,那么上一个DisposeBag
就会被销毁。
这两个东西刚开始看的时候也是一脸懵逼,就知道最好多用observeOn()
,可是不知道为何,下面咱们就来揭开它们的面纱看下它们的真面目吧。
区别其实我感受就一句话,subscribeOn()
设置起点在哪一个线程,observeOn()
设置了后续工做在哪一个线程。例如:
someObservable .doOneThing() // 1 .observeOn(MainRouteScheduler.instance) // 2 .subscribeOn(OtherScheduler.instance) // 3 .subscribeNext { // 4 ...... } .addDisposableTo(disposeBag)
observeOn()
转换线程到主线程,下面全部的操做都在主线程subscribeOn()
规定动做一开始不是发生在默认线程,而是在OtherScheduler
了。observeOn()
,那么这边会在OtherScheduler
发生,可是咱们前面调用了observeOn()
,因此这个动做会在主线程中调用。总结一下:
subscribeOn()
只是影响事件链开始默认的线程,而observeOn()
规定了下一步动做发生在哪一个线程中。
看官方项目里面的Demo
时,我也很疑惑,为何不少的sequence
后面都有shareReplay(1)
呢?想的昏头涨脑。
这里我就给你们讲解一下个人理解吧。先看一个例子:
let disposeBag = DisposeBag() let observable = Observable.just("🤣").map{print($0)} observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag)
打印结果:
🤣 Even:next(()) Even:completed 🤣 Even:next(()) Even:completed
你们发现没有,map()
函数执行了两次,可是有些时候,咱们并不想让map()
函数里面的东西执行两次,好比map()
函数里面执行的是网络请求,我只须要执行一次请求,而后把结果提供给你们使用就好了,多余的请求会增长负担。因此这时候就须要使用shareReplay(1)
了。这里面的数字通常是1
,只执行一次。(ps:我改为 2,3 也只打印一次)
let disposeBag = DisposeBag() let observable = Observable.just("🤣").map{print($0)}.shareReplay(1) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag) observable.subscribe{print("Even:\($0)")}.disposed(by: disposeBag)
打印结果:
🤣 Even:next(()) Even:completed Even:next(()) Even:completed
自定义操做符很简单,官方推荐尽可能使用标准的操做符,可是你也能够定义本身的操做符,文档上说有两种方法,这里介绍一下经常使用的一种方法吧。
例如咱们自定义一个map
操做符:
extension ObserverType { func myMap<R>(transform: E -> R) -> Observable<R> { return Observable.create{ observer in let subscription = self.subscribe {e in switch e{ case .next(let value): let result = transform(value) observer.on(.next(result)) case .error(let error): observer.on(.error(error)) case .completed: observer.on(.completed) } } return subscription } } }
参数是一个闭包,其中闭包参数是E类型返回值是R类型,map函数的返回值是一个Observable
类型。
这又是啥东东? 讲解Driver
以前咱们如今看看下面的🌰:
let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) } results .map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results .bindTo(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
results
,其中flatMapLatest
下面会讲;results
绑定到resultCount.rx.text
上;results
绑定到resultsTableView
上.上面程序会出现下面几个异常:
fetchAutoCompleteItems
出错,那么它绑定的UI将再也不收到任何事件消息;fetchAutoCompleteItems
发生在后台线程,那么它绑定的事件也将在后台线程执行,这样更新UI会形成crash
;fetchAutoCompleteItems
就会执行两次固然针对以上问题,咱们也有解决方案,针对第三点,咱们可使用神器shareReplay(1)
保证只执行一次,可使用observeOn()
保证后面全部操做在主线程完成。
let results = query.rx.text .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) .observeOn(MainScheduler.instance) .catchErrorJustReturn([]) } .shareReplay(1) results .map { "\($0.count)" } .bindTo(resultCount.rx.text) .addDisposableTo(disposeBag) results .bindTo(resultTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
咱们也可使用Driver
来解决:
let results = query.rx.text.asDriver() .throttle(0.3, scheduler: MainScheduler.instance) .flatMapLatest { query in fetchAutoCompleteItems(query) .asDriver(onErrorJustReturn: []) //当碰见错误须要返回什么 } //不须要添加shareReplay(1) results .map { "\($0.count)" } .drive(resultCount.rx.text) //和bingTo()功能同样 .addDisposableTo(disposeBag) results .drive(resultTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in cell.textLabel?.text = "\(result)" } .addDisposableTo(disposeBag)
drive
方法只能在Driver
序列中使用,Driver
有如下特色:
Driver
序列不容许发出error
,Driver
序列的监听只会在主线程中。因此Driver
是专为UI绑定量身打造的东西。
如下状况你可使用Driver
替换BindTo
:
看了前面《RxSwift 系列(四) -- Transforming Operators》,我想你们对于什么时候使用map
和flatMap
也仍是有疑惑。
咱们来看看map
函数和flatMap
函数的定义:map
函数,接收一个R类型的序列,返回一个R类型的序列,仍是原来的序列。
public func map<R>(_ transform: @escaping (Self.E) throws -> R) -> RxSwift.Observable<R>
flatMap
函数,接收一个O类型的序列,返回一个O.E类型的序列,也就是有原来序列里元素组成的新序列。
public func flatMap<O: ObservableConvertibleType>(_ selector: @escaping (E) throws -> O) -> Observable<O.E>
其实这里的map
和flatMap
在swift
中的做用是同样的。map
函数能够对原有序列里面的事件元素进行改造,返回的仍是原来的序列。而flatMap
对原有序列中的元素进行改造和处理,每个元素返回一个新的sequence
,而后把每个元素对应的sequence
合并为一个新的sequence
序列。
看下面例子:
let disposeBag = DisposeBag() let observable = Observable.of("1","2","3","4","5").map{$0 + "scott"} observable.subscribe(onNext: {print($0)}).disposed(by: disposeBag)
打印结果:
1scott 2scott 3scott 4scott 5scott
咱们使用map
对序列中每个元素进行了处理,返回的是一个元素,而使用flatMap
须要返回的序列。那么使用map
也返回一个序列看看。
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
运行结果:
RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String> RxSwift.(Just in _BD9B9D4356C4038796FB16D0D54A9F8E)<Swift.String>
看到结果会打印出每个序列,下面咱们使用merge()
方法将这几个序列进行合并:
let test = Observable.of("1", "2", "3", "4", "5") .map { Observable.just($0) }.merge() test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
运行结果:
1 2 3 4 5
合并为一个新序列后咱们就能够正常打印元素了。下面看看使用faltMap()
函数干这件事:
let test = Observable.of("1", "2", "3", "4", "5") .flatMap { Observable.just($0) } test.subscribe(onNext: { print($0) }) .addDisposableTo(disposeBag)
运行结果:
1 2 3 4 5
看下对比是否是同样,这样子对比就清晰了吧。
flatMap
函数在实际应用中有不少地方须要用到,好比网络请求,网络请求可能会发生错误,咱们须要对这个请求过程进行监听,而后处理错误。只要继续他返回的是一个新的序列。
UIBindingObserver
这个东西颇有用的,建立咱们本身的监听者,有时候RxCocoa
(RxSwift
中对UIKit
的一个扩展库)给的扩展不够咱们使用,好比一个UITextField
有个isEnabled
属性,我想把这个isEnabled
变为一个observer
,咱们能够这样作:
extension Reactive where Base: UITextField { var inputEnabled: UIBindingObserver<Base, Result> { return UIBindingObserver(UIElement: base) { textFiled, result in textFiled.isEnabled = result.isValid } } }
UIBindingObserver
是一个类,他的初始化方法中,有两个参数,第一个参数是一个元素自己,第一个参数是一个闭包,闭包参数是元素自己,还有他的一个属性。
public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Swift.Void)
自定义了一个inputEnabled
关联了UITextField
的isEnabled
属性。
本系列文章理论性的东西就算是讲述完了,若是你发现有错误,欢迎交流,共同进步,谢谢。接下来准备写点实战性的,你们准备好!