标签:operator nal name 官方 patch tab dispose err www
看完本系列前面几篇之后,估计大家也还是有点懵逼,本系列前八篇也都是参考RxSwift
官方文档和一些概念做的解读。上几篇文章概念性的东西有点多,一时也是很难全部记住,大家脑子里面知道有这么个概念就行,用的时候,再来查阅一番,慢慢就掌握了。
本篇主要来深入了解一些RxSwift
实战中用到的一些重要知识点,这里面有很多自己的理解,所以不免会有一些错误的地方,还请大家多多交流,如有发现错误的地方,欢迎评论。
Rx
系列的核心就是Observable Sequence
这个相信大家心中已经有所了解了,这里不再啰嗦了,建议大家看看前面几篇文章去了解一下。接下来介绍一些容易混淆和难以理解的概念。
相信大家看前面几篇文章的时候,大量出现这两个东西,为了理解这两个东西,我们先来简单介绍下观察者模式吧。
比如一个宝宝在睡觉,爸爸妈妈不可能时时刻刻待在那看着吧?那样子太累
了。他们该做啥做啥,只要听到宝宝哭声的时候,他们给宝宝喂奶就行了。这就是一个简单的观察者模式。宝宝是被观察者,爸爸妈妈是观察者也称作订阅者,只要被观察者发出了某一个事件,比如宝宝哭声,叫声都是一个事件,订阅者就会做出相应地响应。
理解了观察者模式这两个概念就很好理解了,Observable
就是可被观察的,也就是我们说的宝宝,他也是事件源。而Observer
就是我们的观察者,也就是当收到事件的时候去做某些处理的爸爸妈妈。观察者需要去订阅(subscribe
)被观察者,才能收到Observable
的事件通知消息。
subscribe
是订阅sequence
发出的事件,比如next
事件,error
事件等。而subscribe(onNext:)
是监听sequence
发出的next
事件中的element
进行处理,他会忽略error
和completed
事件。相对应的还有subscribe(onError:)
和 subscribe(onCompleted:)
。
当监听一个sequence
的时候,有消息事件来了,我们做某些事情。但是这个sequence
不再发送消息事件了,那么我们的监听也就没有什么存在的价值了,所以我们需要释放我们这些监听资源,其实也就是内存资源释放。
释放某一个监听的时候,我们有两种方式处理:
我们可以手动调用释放方式,但是我们一般不适用这种方式。
// 关于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()
打印结果:
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
属性。
本系列文章理论性的东西就算是讲述完了,如果你发现有错误,欢迎交流,共同进步,谢谢。接下来准备写点实战性的,大家准备好!
标签:operator nal name 官方 patch tab dispose err www
原文地址:http://www.cnblogs.com/yujihaia/p/7425687.html