學(xué)習(xí)RxSwift & MVVM-C(一)

函數(shù)響應(yīng)式編程FRP

之前學(xué)習(xí)過Objective-C下的FRP第三方開源框架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)聽并抽象轉(zhuǎn)換成事件流,封裝在Signal(信號(hào))中,我們通過對(duì)Signal的Subscribe(訂閱)就能獲取到其中的事件流,并進(jìn)行相應(yīng)的操作。

函數(shù)式編程 Functional programming

總結(jié)一下函數(shù)式編程具有以下幾個(gè)特點(diǎn):

  • 函數(shù)是“第一等公民”
  • 閉包和高階函數(shù)
  • 只用“表達(dá)式”,不用“語句”,不改變狀態(tài),沒有副作用
  • 柯里化

Haskell是一種標(biāo)準(zhǔn)化的、通用純函數(shù)式編程語言,有非限定性語義和強(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涵蓋了很多語言。

RxSwift為ReactiveX(Reactive Extensions)旗下的Swift語言庫,提供了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開發(fā)時(shí),通常會(huì)引入另外一個(gè)庫:RxCocoa,這個(gè)庫將UIKit以及Foundation框架中許多成員,如視圖(View)、控制事件(Control Event)、鍵值觀察(KVO)、通知(Notification)等等進(jìn)行與RxSwift接入的擴(kuò)展,將Rx與iOS API無縫連接。

Observable 和 Observer

Observable指的是可被觀察者或者事件源。Observable是一種像信號(hào)一樣的序列,可以理解為一串?dāng)?shù)組,但是需要有人監(jiān)聽這些信號(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()

常見用法

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è)可觀察到的序列,它無限地釋放給定的元素

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è)沒有任何元素的可觀察序列,并立即以錯(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)聽,一個(gè)Subject既可以充當(dāng)Obserable也可以充當(dāng)Observer,即它可以發(fā)出事件,也可以監(jiān)聽事件。
在被訂閱以后,訂閱者可以監(jiān)聽到訂閱之后發(fā)生的事件,像收音機(jī)發(fā)送廣播一樣,打開收音機(jī)以后可以聽到當(dāng)前時(shí)間以及之后的廣播,之前未收聽到能否接收的根據(jù)Subject類型判斷。
幾種 Subject 都是 RxSwift.Observable<Element> 的子類。

  • PublishSubject

采用PublishSubject訂閱事件的時(shí)候,只能接收到訂閱他之后發(fā)生的事件,完全和收音機(jī)聽廣播一樣。

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)聽,會(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ì)列,意思就是說會(huì)監(jiān)聽原隊(duì)列,也會(huì)監(jiān)聽你閉包中轉(zhuǎn)換過的隊(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)聽了兩個(gè)組合隊(duì)列

????.score.value = 95 // Will be printed when using flatMap, but will not be printed when using flatMapLatest

????.score.value = 100
  • scan

以初始值開始,然后將一個(gè)累加器閉包應(yīng)用于可觀察序列所發(fā)射的每個(gè)元素,并將每個(gè)中間結(jié)果作為一個(gè)元素可觀察序列返回??蠢?/p>

Observable.of(10, 100, 1000)
    .scan(1) { aggregateValue, newValue in    // aggregateValue 之前返回的值
        aggregateValue + newValue
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)    //  11,111,1111

過濾操作

  • filter

類似于 swift 語法中的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

從序列的開頭獲取指定數(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í),則從可觀察序列的開頭發(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

從序列的開頭跳過指定數(shù)量的元素。

Observable.of("??", "??", "??", "??", "??", "??")
    .skip(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)  // ????????
  • skipWhile

從序列的開頭跳過滿足條件的元素。

Observable.of(1, 2, 3, 4, 5, 6)
    .skipWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)   // 4 5 6
  • skipUntil

從源序列中跳過元素,直到引用可觀察序列發(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

參見下一篇文章

http://m.itdecent.cn/p/d1386dc7617d

CSDN博客地址:
https://blog.csdn.net/wf96390/article/details/88086808

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近在學(xué)習(xí)RxSwift相關(guān)的內(nèi)容,在這里記錄一些基本的知識(shí)點(diǎn),以便今后查閱。 Observable 在RxSwi...
    L_Zephyr閱讀 1,918評(píng)論 1 4
  • 1.RxSwift初識(shí) RxSwift是 ReactiveX 的swift版本,是一種函數(shù)式響應(yīng)編程的框架。RxS...
    MrMessy閱讀 3,279評(píng)論 0 11
  • 發(fā)現(xiàn) 關(guān)注 消息 RxSwift入坑解讀-你所需要知道的各種概念 沸沸騰關(guān)注 2016.11.27 19:11*字...
    楓葉1234閱讀 2,944評(píng)論 0 2
  • 原創(chuàng) 2017-06-13 RxSwift 這個(gè)框架RP框架相信你應(yīng)該不陌生了,在Objective-C中我們使用...
    一根聰閱讀 6,408評(píng)論 4 14
  • 彈指一揮三十年, 青蔥歲月怡無邊。 懵懂一生蒼茫路, 歌樂不絕聲不斷。 萬丈豪情起麓山, 桃子湖畔醉無眠。 待到漫...
    采葭小妖閱讀 795評(píng)論 9 11

友情鏈接更多精彩內(nèi)容