函數(shù)響應(yīng)式編程FRP
之前學(xué)習(xí)過(guò)Objective-C下的FRP第三方開(kāi)源框架ReactiveCocoa,部分學(xué)習(xí)筆記:
https://blog.csdn.net/wf96390/article/details/50933421
它可以在MVVM架構(gòu)模式中充當(dāng)著View(視圖)層與ViewModel(視圖模型)層之間的Binder(綁定者)角色锄俄,實(shí)現(xiàn)兩個(gè)層之間的同步更新察蹲。在ReactiveCocoa的世界中坎炼,數(shù)據(jù)與屬性的改變扳炬、視圖的操作反饋、方法的調(diào)用等都可以被監(jiān)聽(tīng)并抽象轉(zhuǎn)換成事件流序芦,封裝在Signal(信號(hào))中愧沟,我們通過(guò)對(duì)Signal的Subscribe(訂閱)就能獲取到其中的事件流蔬咬,并進(jìn)行相應(yīng)的操作鲤遥。
函數(shù)式編程 Functional programming
總結(jié)一下函數(shù)式編程具有以下幾個(gè)特點(diǎn):
- 函數(shù)是“第一等公民”
- 閉包和高階函數(shù)
- 只用“表達(dá)式”,不用“語(yǔ)句”林艘,不改變狀態(tài)盖奈,沒(méi)有副作用
- 柯里化
Haskell是一種標(biāo)準(zhǔn)化的、通用純函數(shù)式編程語(yǔ)言北启,有非限定性語(yǔ)義和強(qiáng)靜態(tài)類型卜朗。
響應(yīng)式編程
var b = 1
let c = 2
let a = b + c // a = 3
// 改變 b 的值,a的值會(huì)變嗎咕村?
b = 3
print(a)
在命令式編程環(huán)境中场钉, a = b + c 表示將表達(dá)式的結(jié)果賦給 a,而之后改變 b 或 c的值不會(huì)影響 a懈涛。
但在響應(yīng)式編程中逛万,a的值會(huì)隨著 b或 c的更新而更新。
RxSwift
RxSwift是Reactive下的一個(gè)分支批钠,Reactive涵蓋了很多語(yǔ)言宇植。
RxSwift為ReactiveX(Reactive Extensions)旗下的Swift語(yǔ)言庫(kù),提供了Swift平臺(tái)上進(jìn)行響應(yīng)式編程的解決方案埋心。Rx的重要角色為Observable(被觀察者)和Observer(觀察者)指郁,Observable類似于ReactiveCocoa中的Signal,里面裝有事件流拷呆,供Observer訂閱闲坎。事件流在Rx中與ReactiveCocoa一樣具有三類:Next、Error茬斧、Completed腰懂,代表著繼續(xù)事件、錯(cuò)誤事件项秉、完成事件绣溜。我們?cè)谑褂肦xSwift進(jìn)行iOS開(kāi)發(fā)時(shí),通常會(huì)引入另外一個(gè)庫(kù):RxCocoa娄蔼,這個(gè)庫(kù)將UIKit以及Foundation框架中許多成員怖喻,如視圖(View)、控制事件(Control Event)岁诉、鍵值觀察(KVO)罢防、通知(Notification)等等進(jìn)行與RxSwift接入的擴(kuò)展,將Rx與iOS API無(wú)縫連接唉侄。
Observable 和 Observer
Observable指的是可被觀察者或者事件源。Observable是一種像信號(hào)一樣的序列野建,可以理解為一串?dāng)?shù)組属划,但是需要有人監(jiān)聽(tīng)這些信號(hào)恬叹。ObservableType協(xié)議中定義的subscribe函數(shù)就是一種簡(jiǎn)便的訂閱信號(hào)的一種方法。這里的subscribe函數(shù)就是把消息發(fā)給觀察者同眯。
實(shí)現(xiàn)了 ObservableType 協(xié)議, ObservableType 協(xié)議中定義了很多方法绽昼。
public class Observable<Element> : ObservableType {
public func subscribe<O>(_ observer: O) -> Disposable where Element == O.E, O : ObserverType
public func asObservable() -> RxSwift.Observable<RxSwift.Observable<Element>.E>
}
Observer : 觀察者,觀察者需要訂閱Observable须蜗,才能受到其發(fā)出的事件
subscribe :用于訂閱sequence發(fā)出的事件硅确,相當(dāng)于Swift序列中的遍歷操作(makeIterator)
subscribe(_:)方法接受事件參數(shù),下面這個(gè)例子接受元素參數(shù)
someObservable.subscribe(
onNext: { print("Element:", $0) },
onError: { print("Error:", $0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
Dispose
在使用subscribe訂閱一個(gè)可觀察序列時(shí)明肮,會(huì)返回一個(gè)Disposable類型的對(duì)象菱农。這里的Disposable是一個(gè)協(xié)議,只定義了一個(gè)方法:
public protocol Disposable {
/// Dispose resource.
func dispose()
}
DisposeBag : 類似于 iOS 中的 ARC柿估,會(huì)在適當(dāng)?shù)臅r(shí)候銷毀觀察者循未,自動(dòng)去釋放資源。需要調(diào)用 disposed 方法秫舌,加入到 disposed 中的妖。如果未添加 Xcode 會(huì)提示報(bào)錯(cuò)。
let disposeBag = DisposeBag()
extension Disposable {
/// Adds `self` to `bag`
///
/// - parameter bag: `DisposeBag` to add `self` to.
public func disposed(by bag: RxSwift.DisposeBag)
}
如果需要手動(dòng)銷毀觀察者直接調(diào)用dispose()方法
Observable.empty().subscribe { (str: Event) in
print(“empty:”, str)
}.dispose()
常見(jiàn)用法
Observable 類似 swift 中的序列足陨,可以用數(shù)據(jù)流圖表示
https://rxmarbles.com/
創(chuàng)建Observable
- never
構(gòu)建一個(gè)從不終止和發(fā)出任何事件的隊(duì)列
Observable<String>.never().subscribe { _ in
print("This will never be printed")
}
.disposed(by: disposeBag)
- empty
構(gòu)建一個(gè)空的Observable隊(duì)列嫂粟,只發(fā)出完成事件
Observable<Int>.empty()
.subscribe { event in
print(event)
}
.disposed(by: disposeBag)
- just
構(gòu)建一個(gè)只有一個(gè)元素的Observable隊(duì)列
Observable.just("123")
.subscribe { event in
print(event)
}
.disposed(by: disposeBag)
- of
構(gòu)建一個(gè)擁有固定數(shù)量元素的Observable序列
Observable.of("A", "B", "C", "D")
.subscribe(onNext: { element in
print(element)
})
.disposed(by: disposeBag)
- from
從序列中創(chuàng)建可觀察到的序列,如數(shù)組墨缘、字典或集合星虹。
Observable.from(["??", "??", "??", "??"])
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- create
構(gòu)建一個(gè)自定義的可觀察序列
let myJust = { (element: String) -> Observable<String> in
return Observable.create { observer in
observer.on(.next(element))
observer.on(.completed)
return Disposables.create()
}
}
myJust("??")
.subscribe { print($0) }
.disposed(by: disposeBag)
- range
創(chuàng)建一個(gè)可觀察序列,該序列釋放一系列連續(xù)整數(shù)飒房,然后終止
Observable.range(start: 1, count: 10)
.subscribe { print($0) }
.disposed(by: disposeBag)
- repeatElement
創(chuàng)建一個(gè)可觀察到的序列搁凸,它無(wú)限地釋放給定的元素
Observable.repeatElement("123")
.take(3)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- generate
創(chuàng)建一個(gè)可觀察的序列,只要所提供的條件求值為true狠毯,就生成值护糖。
Observable.generate(
initialState: 0,
condition: { $0 < 3 },
iterate: { $0 + 1 }
)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- error
創(chuàng)建一個(gè)沒(méi)有任何元素的可觀察序列,并立即以錯(cuò)誤結(jié)束嚼松。
Observable<Int>.error(TestError.test)
.subscribe { print($0) }
.disposed(by: disposeBag)
創(chuàng)建Subject
Subjet 類似于ReactiveCocoa中的熱信號(hào)嫡良,可以主動(dòng)發(fā)送信號(hào),也可以被監(jiān)聽(tīng)献酗,一個(gè)Subject既可以充當(dāng)Obserable也可以充當(dāng)Observer寝受,即它可以發(fā)出事件,也可以監(jiān)聽(tīng)事件罕偎。
在被訂閱以后很澄,訂閱者可以監(jiān)聽(tīng)到訂閱之后發(fā)生的事件,像收音機(jī)發(fā)送廣播一樣,打開(kāi)收音機(jī)以后可以聽(tīng)到當(dāng)前時(shí)間以及之后的廣播甩苛,之前未收聽(tīng)到能否接收的根據(jù)Subject類型判斷蹂楣。
幾種 Subject 都是 RxSwift.Observable<Element> 的子類。
- PublishSubject
采用PublishSubject訂閱事件的時(shí)候讯蒲,只能接收到訂閱他之后發(fā)生的事件痊土,完全和收音機(jī)聽(tīng)廣播一樣。
let publicSubject = PublishSubject<String>()
publicSubject.onNext("A")
publicSubject.onNext("B")
publicSubject.subscribe(onNext: {print("publicSubject:",$0)}).addDisposableTo(bag)
publicSubject.onNext("C")
publicSubject.onNext("D")
/**
publicSubject: C
publicSubject: D
**/
- ReplaySubject
可以接收到訂閱他之后以及之前發(fā)生的事件, 對(duì)于要接受幾個(gè)以前的事件取決于bufferSize設(shè)置的大小墨林。
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
// 如果需要接受全部的事件赁酝,則可以使用
// let replaySubject = ReplaySubject<String>.createUnbounded()
replaySubject.onNext("A")
replaySubject.onNext("B")
replaySubject.onNext("C")
replaySubject.onNext("D")
replaySubject.subscribe(onNext: {print("replaySubject:",$0)}).addDisposableTo(bag)
replaySubject.onNext("E")
replaySubject.onNext("F")
replaySubject.onNext("G")
/**
replaySubject: C
replaySubject: D
replaySubject: E
replaySubject: F
replaySubject: G
**/
- BehaviorSubject
采用BehaviorSubject訂閱事件,會(huì)接收到訂閱之前的最后一個(gè)事件以及訂閱之后所有事件旭等。如果在 BehaviorSubject 創(chuàng)建后馬上監(jiān)聽(tīng)酌呆,會(huì)接收到初始值和訂閱之后的事件。
let behavior = BehaviorSubject(value: "behavior")
behavior.onNext("A")
behavior.onNext("B")
behavior.subscribe(onNext: {print("behavior:",$0)}).addDisposableTo(bag)
behavior.onNext("C")
behavior.onNext("D")
/**
behavior: B
behavior: C
behavior: D
**/
- Variable
Variable 是對(duì) BehaviorSubject
的一個(gè)包裝辆雾。BehaviorSubject
不會(huì)因?yàn)殄e(cuò)誤而終止, 但是 variable 會(huì)被釋放肪笋。
Variable當(dāng)成Obserable, 讓訂閱者進(jìn)行訂閱時(shí), 需要asObserable轉(zhuǎn)成Obserable;
Variable發(fā)出事件, 直接修改對(duì)象的value即可;
當(dāng)事件結(jié)束時(shí),Variable會(huì)自動(dòng)發(fā)出completed事件
This concept will be deprecated from RxSwift but offical migration path hasn't been decided yet.
https://github.com/ReactiveX/RxSwift/issues/1501
Current recommended replacement for this API is RxCocoa.BehaviorRelay
because:
-
Variable
isn't a standard cross platform concept, hence it's out of place in RxSwift target. - It doesn't have a counterpart for handling events (
PublishRelay
). It models state only. - It doesn't have a consistent naming with *Relay or other Rx concepts.
- It has an inconsistent memory management model compared to other parts of RxSwift (completes on
deinit
).
let variable = Variable("variable")
variable.value = "A"
variable.value = "B"
variable.asObservable().subscribe { (event:Event<String>) in
print("variable:",event)
}.addDisposableTo(bag)
variable.value = "C"
variable.value = "D"
/**
variable: next(B)
variable: next(C)
variable: next(D)
variable: completed
**/
組合操作
可以將多個(gè)Observables組合成單個(gè)的Observable
- startWith
在Observable源發(fā)出元素前度迂,發(fā)出特定的元素
Observable.of("2", "3")
.startWith("1??")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- merge
將源可觀察序列的元素組合成一個(gè)新的可觀察序列
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
Observable.of(subject1, subject2)
.merge()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("???")
subject1.onNext("???")
subject2.onNext("①")
subject2.onNext("②")
subject1.onNext("??")
subject2.onNext("③")
- zip
將可觀察序列合并成一個(gè)新的可觀察序列
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.zip(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement)"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
stringSubject.onNext("???")
stringSubject.onNext("???")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("??")
intSubject.onNext(3)
- combineLatest
將可觀察隊(duì)列合并成單個(gè)新的可觀察隊(duì)列藤乙,當(dāng)多個(gè)源至少有一個(gè)的時(shí)候發(fā)出信號(hào)
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.combineLatest(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement)"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
stringSubject.onNext("???")
stringSubject.onNext("???")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("??")
當(dāng)然可以將數(shù)組作為參數(shù)
let stringObservable = Observable.just("??")
let fruitObservable = Observable.from(["??", "??", "??"])
let animalObservable = Observable.of("??", "??", "??", "??")
Observable.combineLatest([stringObservable, fruitObservable, animalObservable]) {
"\($0[0]) \($0[1]) \($0[2])"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
如果你采用zip組合,那必須兩個(gè)label都有值改變才會(huì)觸發(fā)惭墓,如果使用combineLatest坛梁,則其中一個(gè)改變就會(huì)出發(fā)。
轉(zhuǎn)換操作
轉(zhuǎn)換可觀察隊(duì)列發(fā)出的Next事件里元素的操作
- map
將轉(zhuǎn)換閉包應(yīng)用于可觀察序列發(fā)出的元素腊凶,并返回已轉(zhuǎn)換元素的一個(gè)新的可觀察序列划咐。
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.map { $0 * $0 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- flatMap
將Observable隊(duì)列發(fā)出的元素轉(zhuǎn)換成一個(gè)新的可觀察隊(duì)列,并將兩者的輸出組合成一個(gè)新的可觀察隊(duì)列钧萍,意思就是說(shuō)會(huì)監(jiān)聽(tīng)原隊(duì)列褐缠,也會(huì)監(jiān)聽(tīng)你閉包中轉(zhuǎn)換過(guò)的隊(duì)列。
struct Player {
var score: Variable<Int>
}
let ???? = Player(score: Variable(80))
let ???? = Player(score: Variable(90))
let player = Variable(????)
player.asObservable()
.flatMap { $0.score.asObservable() } // Change flatMap to flatMapLatest and observe change in printed output
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
????.score.value = 85 // 會(huì)有響應(yīng)
player.value = ???? // 這邊也會(huì)有響應(yīng) 監(jiān)聽(tīng)了兩個(gè)組合隊(duì)列
????.score.value = 95 // Will be printed when using flatMap, but will not be printed when using flatMapLatest
????.score.value = 100
- scan
以初始值開(kāi)始风瘦,然后將一個(gè)累加器閉包應(yīng)用于可觀察序列所發(fā)射的每個(gè)元素队魏,并將每個(gè)中間結(jié)果作為一個(gè)元素可觀察序列返回⊥蛏Γ看例子
Observable.of(10, 100, 1000)
.scan(1) { aggregateValue, newValue in // aggregateValue 之前返回的值
aggregateValue + newValue
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // 11,111,1111
過(guò)濾操作
- filter
類似于 swift 語(yǔ)法中的filter胡桨,篩選出符合條件的信號(hào)
Observable.of(
"??", "??", "??",
"??", "??", "??",
"??", "??", "??")
.filter {
$0 == "??"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
- distinctUntilChanged
直到信號(hào)變化時(shí),才發(fā)出信號(hào)瞬雹,比如使用 textfield 只有內(nèi)容變化時(shí)才發(fā)出信號(hào)
Observable.of("??", "??", "??", "??", "??", "??", "??")
.distinctUntilChanged()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) //??????????
- elementAt
獲取序列中的第某個(gè)信號(hào)
Observable.of("??", "??", "??", "??", "??", "??")
.elementAt(3)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // ??
- single
序列發(fā)出的第一個(gè)元素(或滿足條件的第一個(gè)元素)昧谊。如果序列中有多個(gè)滿足條件的或者不存在滿足條件的,則拋出一個(gè)錯(cuò)誤酗捌。
Observable.of("??", "??", "??", "??", "??", "??")
.single()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
增加判斷條件
Observable.of("??", "??", "??", "??", "??", "??")
.single { $0 == "??" }
.subscribe { print($0) }
.disposed(by: disposeBag) // next(??) completed
Observable.of("??", "??", "??", "??", "??", "??")
.single { $0 == "??" }
.subscribe { print($0) }
.disposed(by: disposeBag) // next(??) error(Sequence contains more than one element.)
Observable.of("??", "??", "??", "??", "??", "??")
.single { $0 == "??" }
.subscribe { print($0) }
.disposed(by: disposeBag) // error(Sequence doesn't contain any elements.)
- take
從序列的開(kāi)頭獲取指定數(shù)量的信號(hào)
Observable.of("??", "??", "??", "??", "??", "??")
.take(3)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // ??????
- takeLast
從序列的結(jié)尾獲取指定數(shù)量的元素呢诬。
Observable.of("??", "??", "??", "??", "??", "??")
.takeLast(3)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // ??????
- takeWhile
在指定的條件滿足時(shí)涌哲,則從可觀察序列的開(kāi)頭發(fā)出元素。
Observable.of(1, 2, 3, 4, 5, 6)
.takeWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // 1 2 3
- takeUntil
從源序列中獲取元素馅巷,直到引用可觀察序列發(fā)出元素結(jié)束
let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()
sourceSequence
.takeUntil(referenceSequence)
.subscribe { print($0) }
.disposed(by: disposeBag)
sourceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??")
referenceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??") // next(??) next(??) next(??) completed
- skip
從序列的開(kāi)頭跳過(guò)指定數(shù)量的元素膛虫。
Observable.of("??", "??", "??", "??", "??", "??")
.skip(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // ????????
- skipWhile
從序列的開(kāi)頭跳過(guò)滿足條件的元素。
Observable.of(1, 2, 3, 4, 5, 6)
.skipWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag) // 4 5 6
- skipUntil
從源序列中跳過(guò)元素钓猬,直到引用可觀察序列發(fā)出元素。
let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()
sourceSequence
.skipUntil(referenceSequence)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
sourceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??")
referenceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??")
sourceSequence.onNext("??") // ??????
MVVM-C
參見(jiàn)下一篇文章
http://www.reibang.com/p/d1386dc7617d
CSDN博客地址:
https://blog.csdn.net/wf96390/article/details/88086808