一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
- 本文主要介绍一些高阶函数的使用和原理
1. 组合操作符
1.1 combineLatest
对于组合操作符我们经常会用到的是比如登陆
let ob1 = nameTextfield.rx.text.orEmpty.map{ (str) -> Bool in
str.count > 6
}
let ob2 = passwordTextField.rx.text.orEmpty.map{ (str) -> Bool in
str.count > 6
}
Observable.combineLatest(ob1, ob2){$0&&$1}
.bind(to: self.sureBtn.rx.isEnabled)
.disposed(by: disposBag)
sureBtn.rx.tap.subscribe(onNext: {()in
print("点击了")
}).disposed(by: disposBag)
当我们的姓名输入框和密码输入框都符合要求是才可以点击.下面的例子更能说明他们的关系
let strSub = PublishSubject<String>()
let intSub = PublishSubject<Int>()
Observable.combineLatest(strSub, intSub) { strElement, intElement in
"\(strElement):\(intElement)"
}.subscribe(onNext:{(combineStr) in
print(combineStr)
})
.disposed(by: disposBag)
strSub.onNext("Hello")
strSub.onNext("this")
strSub.onNext("is")
strSub.onNext("combine")
intSub.onNext(03)
intSub.onNext(25)
strSub.onNext("hello")
打印结果
只会保存后发送的信号,并且组合
信号中,只有都发送了才会到共同订阅的.
- 源码分析
这里是把2个序列转换为一个序列,通过闭包把2个序列的元素element
转换为新的element进行订阅。这里2个组合就是CombineLatest2
,3个的话就是CombineLatest3,依次类推最多8
个。这里我们就以2个为例进行下面的分析
CombineLatest2
首先初始化的时候保存序列1和序列2,并保存组合产生新element的闭包
。以及一个订阅的时候run
的方法。
CombineLatestSink2_
初始化的时候
1.2 zip
相比combineLast
,对于zip也是组合操作信号,不过zip
是会保存我们发送的信号结果,2个都满足的时候会发送,但是是按顺序
进行发送。
let strSub = PublishSubject<String>()
let intSub = PublishSubject<Int>()
Observable.zip(strSub,intSub){strElement, intElement in
"\(strElement):\(intElement)"
}.subscribe(onNext:{print($0)}).disposed(by: disposBag)
intSub.onNext(11)
intSub.onNext(12)
strSub.onNext("test11")
intSub.onNext(13)
strSub.onNext("test12")
strSub.onNext("test13")
运行结果
每次会把单个信号
结果记录下来,之后都满足
的话则再依次
取出进行合并我们要的element,发送给订阅者。
1.3 startWith
startWith
也是合并信号序列,但是订阅的时候是倒叙
的方式,相当于从后往前
发送信号给订阅者
Observable.of(1,2,3,4)
.startWith(5)
.startWith(6)
.startWith(7)
.startWith(8,9,10)
.subscribe(onNext:{print($0)})
.disposed(by: disposBag)
运行结果:
1.4 merge
merge
把来源的可观察序列中的元素组合成一个新的可观察序列
,之后会观察每个源的发送的信号并订阅。相当于一个订阅者订阅了多个
序列,每个序列发送了信号,订阅者都能收到信号。
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
Observable.of(subject1,subject2)
.merge()
.subscribe(onNext:{print($0)})
.disposed(by: disposBag)
subject1.onNext(1)
subject2.onNext(2)
subject2.onNext(3)
运行结果:1,2,3
1.5 switchLatest
switchLatest
就类似我们的switch
开关,通过切换
观察源来订阅不同序列发送的信号,对于没有订阅的
let switchLatestSub1 = BehaviorSubject(value: "1")
let switchLatestSub2 = BehaviorSubject(value: "2")
let switchLatestSub = BehaviorSubject(value: switchLatestSub2)
switchLatestSub.asObservable()
.switchLatest()
.subscribe(onNext:{print($0)})
.disposed(by: disposBag)
switchLatestSub1.onNext("3")
switchLatestSub1.onNext("41")
switchLatestSub1.onNext("45")//打印1,,3,4
switchLatestSub2.onNext("5")
//切换订阅源
switchLatestSub.onNext(switchLatestSub1)//打印5
switchLatestSub2.onNext("6")
switchLatestSub1.onNext("7")
对于我们没有订阅的源,发送信号会保存最新的一次信号,下次切换的时候会收到订阅。
2. 映射操作符
映射操作符和swift
的高阶函数类似
2.1 map
let ob = Observable.of(1,2,3)
ob.map{$0+2}
.subscribe(onNext: {print($0)})
.disposed(by: disposBag)//打印结果3,4,5
发送element前,通过map对元素进行transform
调用函数之后发送到订阅者
2.2 flatMap
在swift中flatMap
会把类似二维的数组
变成一维
,压平
的感觉。而RxSwift中将可观测序列发射的元素转换为可观测序列,并将两个可观测序列的发射合并为一个可观测序列。
//结构体
struct Player {
let score: BehaviorRelay<Int>
init(score:Int) {
self.score = BehaviorRelay(value: score)
}
}
let playerA = Player.init(score: 20)
let playerB = Player.init(score: 30)
let player = BehaviorRelay(value: playerA)
player.asObservable()
.flatMap { (player:Player) ->Observable<Int> in
player.score.asObservable()
}.subscribe { (num) in
print(num)
} onError: { (error) in
print(error)
} onCompleted: {
print("completed")
} onDisposed: {
print("disposed")
}.disposed(by: disposBag)
playerA.score.accept(40)
playerB.score.accept(50)
player.accept(playerB)
playerB.score.accept(60)
playerA.score.accept(70)
打印结果
我们把订阅的类型Player转换了Int
类型Observable序列
进行订阅,最后都订阅了话两个可观测序列的发射合并为一个可观测序列。比如在网络请求中为了在请求一个网络资源后,根据服务器返回的结果对原事件序列进行变换,flatMap
就是最好的选择。
2.3 flatMapLatest
flatMap和flatMapLatest
的区别是,当原序列有新的事件发生的时候,flatMapLast 会自动取消上一个事件的订阅,转到新的事件的订阅上面, flatMap则会订阅全部。
flatMapLatest
实际上是map和switchLatest
操作符的组合。
2.4 scan
从初始就带有一个默认值开始,然后对可观察序列发出的每个元素应用累加器闭包
,并以单个元素可观察序列的形式返回每个中间结果
Observable.of(10,20,30,40)
.scan(2) { aggregateValue, newValue in
aggregateValue + newValue// 类似迭代器,不断累加 10+2, 10+2+20,10+2+20+30,10+2+20+30+40
}
.subscribe(onNext:{print($0)})
.disposed(by: disposBag)
打印结果
3. 过滤条件操作符
3.1 filter
filter
和swift类似,在rx中,在发送序列element
的时候先
判断是否满足我们的判读条件
,满足的话进行发送,否则不发送
Observable.of(1,2,3,4,5)
.filter{$0%2 > 0}
.subscribe(onNext: {print($0)})
.disposed(by: disposBag)
打印结果:1,3,5
3.2 distinctUntilChanged
distinctUntilChanged:
序列元素发生改变的时候,类似于去重发送
信号
Observable<Int>.of(1,2,2,3,3,3,4,4,4,4)
.distinctUntilChanged()
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
打印结果1,2,3,4.
3.3 elementAt
elementAt:
仅在可观察序列发出的所有元素的指定索引
处发出元素
Observable<Int>.of(1,2,3,4,5)
.element(at: 1)
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
打印结果为2
3.4 single
single: 只发出可观察序列发出的第一个元素
(或满足条件的第一个元素
)。如果可观察序列发出多个元素,将抛出一个错误。
Observable.of(1,2,3,4,5)
.single()
.subscribe { num in
print(num)
} onError: { error in
print(error)
} onCompleted: {
print("comleter")
} onDisposed: {
print("disposed")
}
.disposed(by: disposeBag)
打印结果:
因为多个条件满足所以会发送error
信号,我们添加判断条件
single
表示单个信号,如果多个条件满足就会发送error
信号
3.5 take
take:
只从一个可观察序列的开始发出指定数量的元素
。 上面signal只有一个序列 在实际开发会受到局限 这里引出take
想几个就几个
Observable.of(1,2,3,4,5)
.take(3)
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
结果:打印前3个元素
3.6 takeLast
takeLast:
仅从可观察序列的末尾发出指定数量的元素
Observable.of(1,2,3,4,5)
.takeLast(1)
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
打印结构为5,相较于take相当于序列的长度减去我们takeLast()的长度开始发送
3.7 take(while:)
takeWhile:满足指定条件
即可从序列开始发送信号
Observable.of(1,2,3,4,5)
.take(while: {$0 < 4})
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
// 打印结果:1,2,3
从开始的的条件开始发送信号,满足就会发送,一开始就不满足就不会发送
3.8 take(until:)
从源可观察序列发出元素,直到参考可观察序列发出元素
//例子1
Observable.of(1,2,3,4,5)
.take(until: {$0 > 4})
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
//例子2
let source1 = PublishSubject<String>()
let source2 = PublishSubject<String>()
source1.take(until: source2)
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
source1.onNext("hello")
source1.onNext("this")
source1.onNext("is")
source2.onNext("good")
source1.onNext("day")
对于例子1可以看到和上面的take(while:)
相反的效果,它是满足
知道我们的条件
满足就停止
发送信号。对于例子2相当于我们用另一个序列控制
,当soucre2发送了序列信号就终止了source1序列。比如我页面销毁了,就不能发送请求了。
打印结果
3.9 skip
skip:跳过
开始的几次序列信号
Observable.of(1,2,3,4,5)
.skip(1)
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
打印结果:2,3,4,5。 对于我们textFiled经常使用,跳过第一次空的初始化信号。
3.10 skip(while:)
skip(while:):
Observable.of(1,2,3,4,5)
.skip(while: {$0<4})//当发送的序列信号元素不满足就跳过
.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
打印结果:4,5
3.11
let sourceSequence = PublishSubject<Int>()
let relevanceSequence = PublishSubject<Int>()
sourceSequence.skip(until: relevanceSequence)
.subscribe(onNext: {print($0)})
.disposed(by: disposBag)
sourceSequence.onNext(1)
sourceSequence.onNext(2)
relevanceSequence.onNext(3) //开关,控制
sourceSequence.onNext(4)
sourceSequence.onNext(5)
打印结果:4,5
可以发现和我们的takeUntil
相反,skip(until:)
是收到关联序列发送序号后该序列才会收到订阅的信号,类似一个开关
。
4. 总结
以上就是部分高阶函数的使用主要分为组合操作符
,映射操作符
,和过滤操作符
,记录下,未完待续。。。