RxSwift (十) 基礎使用篇 1- 序列,訂閱,銷燬

Rxswift(一)函數響應式編程思想git

RxSwift (二)序列核心邏輯分析github

RxSwift (三)Observable的建立,訂閱,銷燬編程

RxSwift(四)高階函數json

RxSwift(五)(Rxswift對比swift,oc用法)swift

Rxswift (六)銷燬者Dispose源碼分析數組

RxSwift(七)Rxswift對比swift用法緩存

RxSwift (十) 基礎使用篇 1- 序列,訂閱,銷燬bash

RxSwift學習之十二 (基礎使用篇 3- UI控件擴展)markdown

1. RxSwift簡介

  • RxSwift 的做用

1)在編寫代碼時咱們常常會須要檢測某些值的變化(好比:textFiled 輸入值的變化、數據請求完成或失敗的變化),而後進行相應的處理。 過去針對不一樣的狀況,咱們須要採用不一樣的事件傳遞方法去處理,好比:delegate、notification、target-action、KVO 等等。 而 RectiveX 機制(由 RxSwift 實現)的出現,讓程序裏的事件傳遞響應方法作到統一。將以前那些經常使用的事件傳遞方法(好比:delegate、notification、target-action 等等),所有替換成 Rx 的「信號鏈」方式。 (2)若是咱們平時使用的是 MVVM 開發模式的話,經過 RxSwift 能夠得到更加方便的數據綁定的方法,使得 MVVM 開發更加如虎添翼。網絡

  • RxSwift 與 RxCocoa

RxSwift:它只是基於 Swift 語言的 Rx 標準實現接口庫,因此 RxSwift 裏不包含任何 Cocoa 或者 UI 方面的類。 RxCocoa:是基於 RxSwift 針對於 iOS 開發的一個庫,它經過 Extension 的方法給原生的好比 UI 控件添加了 Rx 的特性,使得咱們更容易訂閱和響應這些控件的事件。

2. RxSwift簡單使用

2.1 響應式編程與傳統式編程的比較樣例

  • 實例2.1
  1. 有這麼一個需求: 表格中顯示的是歌曲信息(歌名,以及歌手) 點擊選中任意一個單元格,在控制檯中打印出對應的歌曲信息。
    在這裏插入圖片描述
  2. 按傳統方式:首先咱們建立一個 Music 的結構體,用來保存歌曲名稱、歌手名字。此外它還遵循 CustomStringConvertible 協議,方便咱們輸出調試。
import UIKit
 
//歌曲結構體
struct Music {
    let name: String //歌名
    let singer: String //演唱者
     
    init(name: String, singer: String) {
        self.name = name
        self.singer = singer
    }
}
 
//實現 CustomStringConvertible 協議,方便輸出調試
extension Music: CustomStringConvertible {
    var description: String {
        return "name:\(name) singer:\(singer)"
    }
}
複製代碼

2.1.1 傳統編程

  1. 首先寫一個 ViewModel
import Foundation
 
//歌曲列表數據源
struct MusicListViewModel {
    let data = [
        Music(name: "無條件", singer: "陳奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "從前的我", singer: "陳潔儀"),
        Music(name: "在木星", singer: "朴樹"),
    ]
}
複製代碼
  1. 視圖控制器代碼(ViewController.swift)
  • 接着咱們設置 UITableView 的委託,並讓視圖控制器實現 UITableViewDataSource 和 UITableViewDelegate 協議,及相關的協議方法。
  • 這個你們確定都寫過無數遍了,也沒什麼好講的。算一下,這裏一共須要 43 行代碼。
import UIKit
import RxSwift
 
class ViewController: UIViewController {
 
    //tableView對象
    @IBOutlet weak var tableView: UITableView!
     
    //歌曲列表數據源
    let musicListViewModel = MusicListViewModel()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //設置代理
        tableView.dataSource = self
        tableView.delegate = self
    }
}
 
extension ViewController: UITableViewDataSource {
    //返回單元格數量
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return musicListViewModel.data.count
    }
     
    //返回對應的單元格
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")!
        let music = musicListViewModel.data[indexPath.row]
        cell.textLabel?.text = music.name
        cell.detailTextLabel?.text = music.singer
        return cell
    }
}
 
extension ViewController: UITableViewDelegate {
    //單元格點擊
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("你選中的歌曲信息【\(musicListViewModel.data[indexPath.row])】")
    }
}
複製代碼
  • 下面來看一下Rxswift的編程

2.1.2 Rxswift編程

  1. ViewModel 作些修改
    • 這裏咱們將 data 屬性變成一個可觀察序列對象(Observable Squence),而對象當中的內容和咱們以前在數組當中所包含的內容是徹底同樣的。
    • 關於可觀察序列對象在後面的文章中我會詳細介紹。簡單說就是「序列」能夠對這些數值進行「訂閱(Subscribe)」,有點相似於「通知(NotificationCenter)」
import RxSwift
 
//歌曲列表數據源
struct MusicListViewModel {
    let data = Observable.just([
        Music(name: "無條件", singer: "陳奕迅"),
        Music(name: "你曾是少年", singer: "S.H.E"),
        Music(name: "從前的我", singer: "陳潔儀"),
        Music(name: "在木星", singer: "朴樹"),
    ])
}
複製代碼
  1. 視圖控制器代碼(ViewController.swift
    • 這裏咱們再也不須要實現數據源和委託協議了。而是寫一些響應式代碼,讓它們將數據和 UITableView 創建綁定關係。
    • 算了下這裏咱們只須要 31 行代碼,同以前的相比,一下減小了 1/4 代碼量。並且代碼也更清爽了些。
    • 代碼的簡單說明: DisposeBag:做用是 Rx 在視圖控制器或者其持有者將要銷燬的時候,自動釋法掉綁定在它上面的資源。它是經過相似「訂閱處置機制」方式實現(相似於 NotificationCenter 的 removeObserver)。 rx.items(cellIdentifier:):這是 Rx 基於 cellForRowAt 數據源方法的一個封裝。傳統方式中咱們還要有個 numberOfRowsInSection 方法,使用 Rx 後就再也不須要了(Rx 已經幫咱們完成了相關工做)。 rx.modelSelected: 這是 Rx 基於 UITableView 委託回調方法 didSelectRowAt 的一個封裝。

import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
 
    //tableView對象
    @IBOutlet weak var tableView: UITableView!
     
    //歌曲列表數據源
    let musicListViewModel = MusicListViewModel()
     
    //負責對象銷燬
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //將數據源數據綁定到tableView上
        musicListViewModel.data
            .bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in
                cell.textLabel?.text = music.name
                cell.detailTextLabel?.text = music.singer
            }.disposed(by: disposeBag)
         
        //tableView點擊響應
        tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
            print("你選中的歌曲信息【\(music)】")
        }).disposed(by: disposeBag)
    }
}
複製代碼

2.2 Observable介紹、建立可觀察序列

  • Observable 做爲 Rx 的根基,咱們首先對它要有一些基本的瞭解。

  • Observable:

    Observable 這個類就是 Rx 框架的基礎,咱們能夠稱它爲可觀察序列。它的做用就是能夠異步地產生一系列的 Event(事件),即一個 Observable 對象會隨着時間推移不按期地發出 event(element : T) 這樣一個東西。 並且這些 Event 還能夠攜帶數據,它的泛型 就是用來指定這個 Event 攜帶的數據的類型。 有了可觀察序列,咱們還須要有一個 Observer(訂閱者)來訂閱它,這樣這個訂閱者才能收到 Observable 不時發出的 Event。

  • Event

  • 查看 RxSwift 源碼能夠發現,事件 Event 的定義以下:

public enum Event<Element> {
    /// Next element is produced.
    case next(Element)
 
    /// Sequence terminated with an error.
    case error(Swift.Error)
 
    /// Sequence completed successfully.
    case completed
}
複製代碼
  • 能夠看到 Event 就是一個枚舉,也就是說一個 Observable 是能夠發出 3 種不一樣類型的 Event 事件:
    • nextnext 事件就是那個能夠攜帶數據 的事件,能夠說它就是一個「最正常」的事件。
    • errorerror 事件表示一個錯誤,它能夠攜帶具體的錯誤內容,一旦 Observable 發出了 error event,則這個 Observable 就等於終止了,之後它不再會發出 event 事件了。
    • completedcompleted 事件表示 Observable 發出的事件正常地結束了,跟 error 同樣,一旦 Observable 發出了 completed event,則這個 Observable 就等於終止了,之後它不再會發出 event 事件了。

2.2.1 Observable 與 Sequence比較

  • 1)爲更好地理解,咱們能夠把每個 Observable 的實例想象成於一個 Swift 中的 Sequence:
    • 即一個 ObservableObservableType)至關於一個序列 SequenceSequenceType)。
    • ObservableType.subscribe(_:) 方法其實就至關於 SequenceType.generate()
  • 2)但它們之間仍是有許多區別的:
    • Swift 中的 SequenceType 是同步的循環,而 Observable 是異步的。
    • Observable 對象會在有任何 Event 時候,自動將 Event 做爲一個參數經過 ObservableType.subscribe(_:) 發出,並不須要使用 next 方法。

2.2.2 建立 Observable 序列

  • 咱們能夠經過以下幾種方法來建立一個 Observable 序列
  1. just() 方法

(1)該方法經過傳入一個默認值來初始化。 (2)下面樣例咱們顯式地標註出了 observable 的類型爲 Observable,即指定了這個 Observable 所發出的事件攜帶的數據類型必須是 Int 類型的。

let observable = Observable<Int>.just(5)
複製代碼
  1. of() 方法

(1)該方法能夠接受可變數量的參數(必須要是同類型的) (2)下面樣例中我沒有顯式地聲明出 Observable 的泛型類型,Swift 也會自動推斷類型。

let observable = Observable.of("A", "B", "C")
複製代碼
  1. from() 方法

(1)該方法須要一個數組參數。 (2)下面樣例中數據裏的元素就會被當作這個 Observable 所發出 event 攜帶的數據內容,最終效果同上面餓 of() 樣例是同樣的。

let observable = Observable.from(["A", "B", "C"])
複製代碼
  1. empty() 方法 該方法建立一個空內容的 Observable 序列。
let observable = Observable<Int>.never()
複製代碼
  1. never() 方法 該方法建立一個永遠不會發出 Event(也不會終止)的 Observable 序列。
let observable = Observable<Int>.never()
複製代碼
  1. error() 方法 該方法建立一個不作任何操做,而是直接發送一個錯誤的 Observable 序列。
enum MyError: Error {
    case A
    case B
}
         
let observable = Observable<Int>.error(MyError.A)
複製代碼
  1. range() 方法

(1)該方法經過指定起始和結束數值,建立一個以這個範圍內全部值做爲初始值的 Observable 序列。 (2)下面樣例中,兩種方法建立的 Observable 序列都是同樣的。

//使用range()
let observable = Observable.range(start: 1, count: 5)
 
//使用of()
let observable = Observable.of(1, 2, 3 ,4 ,5)
複製代碼
  1. repeatElement() 方法 該方法建立一個能夠無限發出給定元素的 Event 的 Observable 序列(永不終止)。
let observable = Observable.repeatElement(1)
複製代碼
  1. generate() 方法

(1)該方法建立一個只有當提供的全部的判斷條件都爲 true 的時候,纔會給出動做的 Observable 序列。 (2)下面樣例中,兩種方法建立的 Observable 序列都是同樣的。

//使用generate()方法
let observable = Observable.generate(
    initialState: 0,
    condition: { $0 <= 10 },
    iterate: { $0 + 2 }
)
 
//使用of()方法
let observable = Observable.of(0 , 2 ,4 ,6 ,8 ,10)
複製代碼
  1. create() 方法

(1)該方法接受一個 block 形式的參數,任務是對每個過來的訂閱進行處理。 (2)下面是一個簡單的樣例。爲方便演示,這裏增長了訂閱相關代碼

//這個block有一個回調參數observer就是訂閱這個Observable對象的訂閱者
//當一個訂閱者訂閱這個Observable對象的時候,就會將訂閱者做爲參數傳入這個block來執行一些內容
let observable = Observable<String>.create{observer in
    //對訂閱者發出了.next事件,且攜帶了一個數據"hangge.com"
    observer.onNext("hangge.com")
    //對訂閱者發出了.completed事件
    observer.onCompleted()
    //由於一個訂閱行爲會有一個Disposable類型的返回值,因此在結尾必定要returen一個Disposable
    return Disposables.create()
}
 
//訂閱測試
observable.subscribe {
    print($0)
}
複製代碼
  1. deferred() 方法

(1)該個方法至關因而建立一個 Observable 工廠,經過傳入一個 block 來執行延遲 Observable 序列建立的行爲,而這個 block 裏就是真正的實例化序列對象的地方。 (2)下面是一個簡單的演示樣例:

//用於標記是奇數、仍是偶數
var isOdd = true
 
//使用deferred()方法延遲Observable序列的初始化,經過傳入的block來實現Observable序列的初始化而且返回。
let factory : Observable<Int> = Observable.deferred {
     
    //讓每次執行這個block時候都會讓奇、偶數進行交替
    isOdd = !isOdd
     
    //根據isOdd參數,決定建立並返回的是奇數Observable、仍是偶數Observable
    if isOdd {
        return Observable.of(1, 3, 5 ,7)
    }else {
        return Observable.of(2, 4, 6, 8)
    }
}
 
//第1次訂閱測試
factory.subscribe { event in
    print("\(isOdd)", event)
}
 
//第2次訂閱測試
factory.subscribe { event in
    print("\(isOdd)", event)
}
複製代碼

運行結果以下,能夠看到咱們兩次訂閱的獲得的 Observable 是不同的:

在這裏插入圖片描述
13. interval() 方法

(1)這個方法建立的 Observable 序列每隔一段設定的時間,會發出一個索引數的元素。並且它會一直髮送下去。 (2)下面方法讓其每 1 秒發送一次,而且是在主線程(MainScheduler)發送。

let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}
複製代碼
  1. timer() 方法
  • (1) 這個方法有兩種用法,一種是建立的 Observable 序列在通過設定的一段時間後,產生惟一的一個元素。
//5秒種後發出惟一的一個元素0
let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}
複製代碼
  • (2) 另外一種是建立的 Observable 序列在通過設定的一段時間後,每隔一段時間產生一個元素。
//延時5秒種後,每隔1秒鐘發出一個元素
let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance)
observable.subscribe { event in
    print(event)
}
複製代碼

2.3 Observable訂閱、事件監聽、訂閱銷燬

2.3.1 Observable訂閱

  • 有了 Observable,咱們還要使用 subscribe() 方法來訂閱它,接收它發出的 Event。

2.3.1.1 第一種訂閱方式

  • (1)咱們使用 subscribe() 訂閱了一個 Observable 對象,該方法的 block 的回調參數就是被髮出的 event 事件,咱們將其直接打印出來。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe { event in
    print(event)
}
複製代碼

運行結果以下,能夠看到:

image

初始化 Observable 序列時設置的默認值都按順序經過 .next 事件發送出來。 當 Observable 序列的初始數據都發送完畢,它還會自動發一個 .completed 事件出來。

  • (2)若是想要獲取到這個事件裏的數據,能夠經過 event.element 獲得。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe { event in
    print(event.element)
}
複製代碼

運行結果以下:

image

2.3.1.2 第二種訂閱方式

  • (1)RxSwift 還提供了另外一個 subscribe 方法,它能夠把 event 進行分類:
    • 經過不一樣的 block 回調處理不一樣類型的 event。(其中 onDisposed 表示訂閱行爲被 dispose 後的回調)
    • 同時會把 event 攜帶的數據直接解包出來做爲參數,方便咱們使用。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe(onNext: { element in
    print(element)
}, onError: { error in
    print(error)
}, onCompleted: {
    print("completed")
}, onDisposed: {
    print("disposed")
})
複製代碼

運行結果以下:

image

  • (2)subscribe() 方法的 onNextonErroronCompletedonDisposed 這四個回調 block 參數都是有默認值的,即它們都是可選的。因此咱們也能夠只處理 onNext 而無論其餘的狀況。
let observable = Observable.of("A", "B", "C")
         
observable.subscribe(onNext: { element in
    print(element)
})
複製代碼

運行結果以下: A B C

2.3.2 事件監聽

  • doOn 介紹

    (1)咱們可使用 doOn 方法來監聽事件的生命週期,它會在每一次事件發送前被調用。 (2)同時它和 subscribe 同樣,能夠經過不一樣的 block 回調處理不一樣類型的 event。好比: do(onNext:) 方法就是在 subscribe(onNext:) 前調用 而 do(onCompleted:) 方法則會在 subscribe(onCompleted:) 前面調用。

  • 使用樣例

let observable = Observable.of("A", "B", "C")
 
observable
    .do(onNext: { element in
        print("Intercepted Next:", element)
    }, onError: { error in
        print("Intercepted Error:", error)
    }, onCompleted: {
        print("Intercepted Completed")
    }, onDispose: {
        print("Intercepted Disposed")
    })
    .subscribe(onNext: { element in
        print(element)
    }, onError: { error in
        print(error)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })
複製代碼

2.3.3 訂閱銷燬

2.3.3.1 Observable 從建立到終結流程

  • (1)一個 Observable 序列被建立出來後它不會立刻就開始被激活從而發出 Event,而是要等到它被某我的訂閱了纔會激活它。
  • (2)而 Observable 序列激活以後要一直等到它發出了 .error 或者 .completedevent 後,它才被終結。

2.3.3.2 dispose() 方法

-(1)使用該方法咱們能夠手動取消一個訂閱行爲。 -(2)若是咱們以爲這個訂閱結束了再也不須要了,就能夠調用 dispose() 方法把這個訂閱給銷燬掉,防止內存泄漏。 -(3)當一個訂閱行爲被 dispose 了,那麼以後 observable 若是再發出 event,這個已經 dispose 的訂閱就收不到消息了。下面是一個簡單的使用樣例。

let observable = Observable.of("A", "B", "C")
         
//使用subscription常量存儲這個訂閱方法
let subscription = observable.subscribe { event in
    print(event)
}
         
//調用這個訂閱的dispose()方法
subscription.dispose()
複製代碼

2.3.3.13 DisposeBag

  • (1)除了 dispose() 方法以外,咱們更常常用到的是一個叫 DisposeBag 的對象來管理多個訂閱行爲的銷燬:
  1. 咱們能夠把一個 DisposeBag 對象當作一個垃圾袋,把用過的訂閱行爲都放進去。
  2. 而這個 DisposeBag 就會在本身快要 dealloc 的時候,對它裏面的全部訂閱行爲都調用 dispose() 方法。
  • (2)下面是一個簡單的使用樣例。
let disposeBag = DisposeBag()
         
//第1個Observable,及其訂閱
let observable1 = Observable.of("A", "B", "C")
observable1.subscribe { event in
    print(event)
}.disposed(by: disposeBag)
 
//第2個Observable,及其訂閱
let observable2 = Observable.of(1, 2, 3)
observable2.subscribe { event in
    print(event)
}.disposed(by: disposeBag)
複製代碼

2.4 AnyObserver、Binder

2.4.1 觀察者(Observer)

  • 觀察者(Observer)的做用就是監聽事件,而後對這個事件作出響應。或者說任何響應事件的行爲都是觀察者。好比:
    • 當咱們點擊按鈕,彈出一個提示框。那麼這個「彈出一個提示框」就是觀察者 Observer
    • 當咱們請求一個遠程的 json 數據後,將其打印出來。那麼這個「打印 json 數據」就是觀察者 Observer

image

2.4.2 建立觀察者

2.4.2.1 直接在 subscribe、bind 方法中建立觀察者

  1. 在 subscribe 方法中建立

(1)建立觀察者最直接的方法就是在 Observable 的 subscribe 方法後面描述當事件發生時,須要如何作出響應。 (2)好比下面的樣例,觀察者就是由後面的 onNext,onError,onCompleted 這些閉包構建出來的。

let observable = Observable.of("A", "B", "C")
          
observable.subscribe(onNext: { element in
    print(element)
}, onError: { error in
    print(error)
}, onCompleted: {
    print("completed")
})
複製代碼

運行結果:

image

  1. 在 bind 方法中建立 (1)下面代碼咱們建立一個定時生成索引數的 Observable 序列,並將索引數不斷顯示在 label 標籤上:
//Observable序列(每隔1秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
 
        observable
            .map { "當前索引數:\($0 )"}
            .bind { [weak self](text) in
                //收到發出的索引數後顯示到label上
                self?.label.text = text
            }
            .disposed(by: disposeBag)
複製代碼

2.4.2.2 使用 AnyObserver 建立觀察者

  • AnyObserver 能夠用來描敘任意一種觀察者。
  • 配合 subscribe 方法使用
//觀察者
let observer: AnyObserver<String> = AnyObserver { (event) in
    switch event {
    case .next(let data):
        print(data)
    case .error(let error):
        print(error)
    case .completed:
        print("completed")
    }
}
 
let observable = Observable.of("A", "B", "C")
observable.subscribe(observer)
複製代碼
  • 配合 bindTo 方法使用
//觀察者
        let observer: AnyObserver<String> = AnyObserver { [weak self] (event) in
            switch event {
            case .next(let text):
                //收到發出的索引數後顯示到label上
                self?.label.text = text
            default:
                break
            }
        }
         
        //Observable序列(每隔1秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當前索引數:\($0 )"}
            .bind(to: observer)
            .disposed(by: disposeBag)
    }
複製代碼

2.4.2.3 使用 Binder 建立觀察者

(1)相較於 AnyObserver 的大而全,Binder 更專一於特定的場景。Binder 主要有如下兩個特徵: 不會處理錯誤事件 確保綁定都是在給定 Scheduler 上執行(默認 MainScheduler) (2)一旦產生錯誤事件,在調試環境下將執行 fatalError,在發佈環境下將打印錯誤信息。

  • 實例2.4.2.3
//觀察者
        let observer: Binder<String> = Binder(label) { (view, text) in
            //收到發出的索引數後顯示到label上
            view.text = text
        }
         
        //Observable序列(每隔1秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當前索引數:\($0 )"}
            .bind(to: observer)
            .disposed(by: disposeBag)

//Observable序列(每隔1秒鐘發出一個索引數)
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
    .map { $0 % 2 == 0 }
    .bind(to: button.rx.isEnabled)
    .disposed(by: disposeBag)

複製代碼

2.5 自定義可綁定屬性

  • 有時咱們想讓 UI 控件建立出來後默認就有一些觀察者,而沒必要每次都爲它們單獨去建立觀察者。好比咱們想要讓全部的 UIlabel 都有個 fontSize 可綁定屬性,它會根據事件值自動改變標籤的字體大小。

  • 經過對 UI 類進行擴展 這裏咱們經過對 UILabel 進行擴展,增長了一個 fontSize 可綁定屬性。

import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    @IBOutlet weak var label: UILabel!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        //Observable序列(每隔0.5秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
        observable
            .map { CGFloat($0) }
            .bind(to: label.fontSize) //根據索引數不斷變放大字體
            .disposed(by: disposeBag)
    }
}
 
extension UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder(self) { label, fontSize in
            label.font = UIFont.systemFont(ofSize: fontSize)
        }
    }
}
複製代碼
  • 經過對 Reactive 類進行擴展 既然使用了 RxSwift,那麼更規範的寫法應該是對 Reactive 進行擴展。這裏一樣是給 UILabel 增長了一個 fontSize 可綁定屬性。
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    @IBOutlet weak var label: UILabel!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        //Observable序列(每隔0.5秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
        observable
            .map { CGFloat($0) }
            .bind(to: label.rx.fontSize) //根據索引數不斷變放大字體
            .disposed(by: disposeBag)
    }
}
 
extension Reactive where Base: UILabel {
    public var fontSize: Binder<CGFloat> {
        return Binder(self.base) { label, fontSize in
            label.font = UIFont.systemFont(ofSize: fontSize)
        }
    }
}
複製代碼
  • RxSwift 自帶的可綁定屬性(UI 觀察者)

其實 RxSwift 已經爲咱們提供許多經常使用的可綁定屬性。好比 UILabel 就有 text 和 attributedText 這兩個可綁定屬性。

extension Reactive where Base: UILabel {
     
    /// Bindable sink for `text` property.
    public var text: Binder<String?> {
        return Binder(self.base) { label, text in
            label.text = text
        }
    }
 
    /// Bindable sink for `attributedText` property.
    public var attributedText: Binder<NSAttributedString?> {
        return Binder(self.base) { label, text in
            label.attributedText = text
        }
    }
}
複製代碼

那麼上文那個定時顯示索引數的樣例,咱們其實不須要自定義 UI 觀察者,直接使用 RxSwift 提供的綁定屬性便可。

//Observable序列(每隔1秒鐘發出一個索引數)
        let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        observable
            .map { "當前索引數:\($0 )"}
            .bind(to: label.rx.text) //收到發出的索引數後顯示到label上
            .disposed(by: disposeBag)
複製代碼

2.6 Subjects、Variables

當咱們建立一個 Observable 的時候就要預先將要發出的數據都準備好,等到有人訂閱它時再將數據經過 Event 發出去。 但有時咱們但願 Observable 在運行時能動態地「得到」或者說「產生」出一個新的數據,再經過 Event 發送出去。好比:訂閱一個輸入框的輸入內容,當用戶每輸入一個字後,這個輸入框關聯的 Observable 就會發出一個帶有輸入內容的 Event,通知給全部訂閱者。 這個就可使用下面將要介紹的 Subjects 來實現。

2.6.1 Subjects

2.6.1.1 Subjects 簡介

-(1)Subjects 既是訂閱者,也是 Observable: 說它是訂閱者,是由於它可以動態地接收新的值。 說它又是一個 Observable,是由於當 Subjects 有了新的值以後,就會經過 Event 將新值發出給他的全部訂閱者。

-(2)一共有四種 Subjects,分別爲:PublishSubjectBehaviorSubjectReplaySubjectVariable。他們之間既有各自的特色,也有相同之處:

  1. 首先他們都是 Observable,他們的訂閱者都能收到他們發出的新的 Event
  2. 直到 Subject 發出 .complete 或者 .errorEvent 後,該 Subject 便終結了,同時它也就不會再發出 .next 事件。
  3. 對於那些在 Subject 終結後再訂閱他的訂閱者,也能收到 subject 發出的一條 .complete.errorevent,告訴這個新的訂閱者它已經終結了。
  4. .他們之間最大的區別只是在於:當一個新的訂閱者剛訂閱它的時候,能不能收到 Subject 之前發出過的舊 Event,若是能的話又能收到多少個。

-(3)Subject 經常使用的幾個方法:

  1. onNext(:):是 on(.next(:)) 的簡便寫法。該方法至關於 subject 接收到一個 .next 事件。
  2. onError(:):是 on(.error(:)) 的簡便寫法。該方法至關於 subject 接收到一個 .error 事件。
  3. onCompleted():是 on(.completed) 的簡便寫法。該方法至關於 subject 接收到一個 .completed 事件。

2.6.1.2 PublishSubject

  • (1)基本介紹

PublishSubject 是最普通的 Subject,它不須要初始值就能建立。 PublishSubject 的訂閱者從他們開始訂閱的時間點起,能夠收到訂閱後 Subject 發出的新 Event,而不會收到他們在訂閱前已發出的 Event

  • (2)時序圖 以下圖:最上面一條是 PublishSubject。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣,能夠發現 PublishSubject 的訂閱者只能收到他們訂閱後的 Event

image

  • 實例 2.6.1.2 :
let disposeBag = DisposeBag()
 
//建立一個PublishSubject
let subject = PublishSubject<String>()
 
//因爲當前沒有任何訂閱者,因此這條信息不會輸出到控制檯
subject.onNext("111")
 
//第1次訂閱subject
subject.subscribe(onNext: { string in
    print("第1次訂閱:", string)
}, onCompleted:{
    print("第1次訂閱:onCompleted")
}).disposed(by: disposeBag)
 
//當前有1個訂閱,則該信息會輸出到控制檯
subject.onNext("222")
 
//第2次訂閱subject
subject.subscribe(onNext: { string in
    print("第2次訂閱:", string)
}, onCompleted:{
    print("第2次訂閱:onCompleted")
}).disposed(by: disposeBag)
 
//當前有2個訂閱,則該信息會輸出到控制檯
subject.onNext("333")
 
//讓subject結束
subject.onCompleted()
 
//subject完成後會發出.next事件了。
subject.onNext("444")
 
//subject完成後它的全部訂閱(包括結束後的訂閱),都能收到subject的.completed事件,
subject.subscribe(onNext: { string in
    print("第3次訂閱:", string)
}, onCompleted:{
    print("第3次訂閱:onCompleted")
}).disposed(by: disposeBag)
複製代碼

運行結果:

image

2.6.1.3 BehaviorSubject

-(1)基本介紹

BehaviorSubject 須要經過一個默認初始值來建立。 當一個訂閱者來訂閱它的時候,這個訂閱者會當即收到 BehaviorSubjects 上一個發出的 event。以後就跟正常的狀況同樣,它也會接收到 BehaviorSubject 以後發出的新的 event

-(2)時序圖 以下圖:最上面一條是 BehaviorSubject。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣,能夠發現 BehaviorSubject 的訂閱者一開始就能收到 BehaviorSubjects 以前發出的一個 Event

BehaviorSubject

  • 實例 2.6.1.3
let disposeBag = DisposeBag()
 
//建立一個BehaviorSubject
let subject = BehaviorSubject(value: "111")
 
//第1次訂閱subject
subject.subscribe { event in
    print("第1次訂閱:", event)
}.disposed(by: disposeBag)
 
//發送next事件
subject.onNext("222")
 
//發送error事件
subject.onError(NSError(domain: "local", code: 0, userInfo: nil))
 
//第2次訂閱subject
subject.subscribe { event in
    print("第2次訂閱:", event)
}.disposed(by: disposeBag)
複製代碼

運行結果:

image

2.6.1.4 ReplaySubject

  • (1)基本介紹
  1. ReplaySubject 在建立時候須要設置一個 bufferSize,表示它對於它發送過的 event 的緩存個數。
  2. 好比一個 ReplaySubjectbufferSize 設置爲 2,它發出了 3 個 .nextevent,那麼它會將後兩個(最近的兩個)event 給緩存起來。此時若是有一個 subscriber 訂閱了這個 ReplaySubject,那麼這個 subscriber 就會當即收到前面緩存的兩個 .nextevent
  3. 若是一個 subscriber 訂閱已經結束的 ReplaySubject,除了會收到緩存的 .nextevent 外,還會收到那個終結的 .error 或者 .completeevent
  • (2)時序圖 以下圖:最上面一條是 ReplaySubjectbufferSize 設爲爲 2)。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣。能夠發現 ReplaySubject 的訂閱者一開始就能收到 ReplaySubject 以前發出的兩個 Event(若是有的話)。

ReplaySubject

  • 實例 2.6.1.4 :
let disposeBag = DisposeBag()
 
//建立一個bufferSize爲2的ReplaySubject
let subject = ReplaySubject<String>.create(bufferSize: 2)
 
//連續發送3個next事件
subject.onNext("111")
subject.onNext("222")
subject.onNext("333")
 
//第1次訂閱subject
subject.subscribe { event in
    print("第1次訂閱:", event)
}.disposed(by: disposeBag)
 
//再發送1個next事件
subject.onNext("444")
 
//第2次訂閱subject
subject.subscribe { event in
    print("第2次訂閱:", event)
}.disposed(by: disposeBag)
 
//讓subject結束
subject.onCompleted()
 
//第3次訂閱subject
subject.subscribe { event in
    print("第3次訂閱:", event)
}.disposed(by: disposeBag)
複製代碼

運行結果:

image

2.6.2 Variables

  • 注意:因爲 Variable 在以後版本中將被廢棄,建議使用 Varible 的地方都改用下面介紹的 BehaviorRelay 做爲替代

2.6.2.1 Variable

-(1)基本介紹

  1. Variable 其實就是對 BehaviorSubject 的封裝,因此它也必需要經過一個默認的初始值進行建立。
  2. Variable 具備 BehaviorSubject 的功能,可以向它的訂閱者發出上一個 event 以及以後新建立的 event
  3. 不一樣的是,Variable 還把會把當前發出的值保存爲本身的狀態。同時它會在銷燬時自動發送 .completeevent,不須要也不能手動給 Variables 發送 completed 或者 error 事件來結束它。
  4. 簡單地說就是 Variable 有一個 value 屬性,咱們改變這個 value 屬性的值就至關於調用通常 SubjectsonNext() 方法,而這個最新的 onNext() 的值就被保存在 value 屬性裏了,直到咱們再次修改它。
  5. Variables 自己沒有 subscribe() 方法,可是全部 Subjects 都有一個 asObservable() 方法。咱們可使用這個方法返回這個 VariableObservable 類型,拿到這個 Observable 類型咱們就能訂閱它了。
  • 實例 2.6.2.1
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
 
        let disposeBag = DisposeBag()
         
        //建立一個初始值爲111的Variable
        let variable = Variable("111")
         
        //修改value值
        variable.value = "222"
         
        //第1次訂閱
        variable.asObservable().subscribe {
            print("第1次訂閱:", $0)
        }.disposed(by: disposeBag)
         
        //修改value值
        variable.value = "333"
         
        //第2次訂閱
        variable.asObservable().subscribe {
            print("第2次訂閱:", $0)
        }.disposed(by: disposeBag)
         
        //修改value值
        variable.value = "444"
    }
}

注意:因爲 Variable 對象在 viewDidLoad() 方法內初始化,因此它的生命週期就被限制在該方法內。當這個方法執行完畢後,這個 Variable 對象就會被銷燬,同時它也就自動地向它的全部訂閱者發出 .completed 事件
複製代碼

運行結果:

image

2.6.2.2 BehaviorRelay

  • 1)基本介紹
  1. BehaviorRelay 是做爲 Variable 的替代者出現的。它的本質其實也是對 BehaviorSubject 的封裝,因此它也必需要經過一個默認的初始值進行建立。
  2. BehaviorRelay 具備 BehaviorSubject 的功能,可以向它的訂閱者發出上一個 event 以及以後新建立的 event
  3. BehaviorSubject 不一樣的是,不須要也不能手動給 BehaviorReply 發送 completed 或者 error 事件來結束它(BehaviorRelay 會在銷燬時也不會自動發送 .completeevent)。
  4. BehaviorRelay 有一個 value 屬性,咱們經過這個屬性能夠獲取最新值。而經過它的 accept() 方法能夠對值進行修改。
  • (2)上面的 Variable 樣例咱們能夠改用成 BehaviorRelay,代碼以下:
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let disposeBag = DisposeBag()
         
        //建立一個初始值爲111的BehaviorRelay
        let subject = BehaviorRelay<String>(value: "111")
         
        //修改value值
        subject.accept("222")
         
        //第1次訂閱
        subject.asObservable().subscribe {
            print("第1次訂閱:", $0)
            }.disposed(by: disposeBag)
         
        //修改value值
        subject.accept("333")
         
        //第2次訂閱
        subject.asObservable().subscribe {
            print("第2次訂閱:", $0)
            }.disposed(by: disposeBag)
         
        //修改value值
        subject.accept("444")
    }
}
複製代碼

運行結果:

image

  • (3)若是想將新值合併到原值上,能夠經過 accept() 方法與 value 屬性配合來實現。(這個經常使用在表格上拉加載功能上,BehaviorRelay 用來保存全部加載到的數據)
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let disposeBag = DisposeBag()
         
        //建立一個初始值爲包含一個元素的數組的BehaviorRelay
        let subject = BehaviorRelay<[String]>(value: ["1"])
         
        //修改value值
        subject.accept(subject.value + ["2", "3"])
         
        //第1次訂閱
        subject.asObservable().subscribe {
            print("第1次訂閱:", $0)
            }.disposed(by: disposeBag)
         
        //修改value值
        subject.accept(subject.value + ["4", "5"])
         
        //第2次訂閱
        subject.asObservable().subscribe {
            print("第2次訂閱:", $0)
            }.disposed(by: disposeBag)
         
        //修改value值
        subject.accept(subject.value + ["6", "7"])
    }
}
複製代碼

運行結果:

image

2.7 變換操做符:buffer、map、flatMap、scan等

  • 變換操做指的是對原始的 Observable 序列進行一些轉換,相似於 Swift 中 CollectionType 的各類轉換。

2.7.1 buffer

  • (1)基本介紹

buffer 方法做用是緩衝組合,第一個參數是緩衝時間,第二個參數是緩衝個數,第三個參數是線程。 該方法簡單來講就是緩存 Observable 中發出的新元素,當元素達到某個數量,或者通過了特定的時間,它就會將這個元素集合發送出來。

buffer

  • 實例 2.7.1
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
 
        let subject = PublishSubject<String>()
 
        //每緩存3個元素則組合起來一塊兒發出。
        //若是1秒鐘內不夠3個也會發出(有幾個發幾個,一個都沒有發空數組 [])
        subject
            .buffer(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
 
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
         
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
}
複製代碼

運行結果:

image

2.7.2 window

  • (1)基本介紹

window 操做符和 buffer 十分類似。不過 buffer 是週期性的將緩存的元素集合發送出來,而 window 週期性的將元素集合以 Observable 的形態發送出來。 同時 buffer 要等到元素蒐集完畢後,纔會發出元素序列。而 window 能夠實時發出元素序列。

window

  • 實例 2.7.2
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
         
        let subject = PublishSubject<String>()
         
        //每3個元素做爲一個子Observable發出。
        subject
            .window(timeSpan: 1, count: 3, scheduler: MainScheduler.instance)
            .subscribe(onNext: { [weak self]  in
                print("subscribe: \($0)")
                $0.asObservable()
                    .subscribe(onNext: { print($0) })
                    .disposed(by: self!.disposeBag)
            })
            .disposed(by: disposeBag)
         
        subject.onNext("a")
        subject.onNext("b")
        subject.onNext("c")
         
        subject.onNext("1")
        subject.onNext("2")
        subject.onNext("3")
    }
}
複製代碼

運行結果:

image

2.7.3 map

  • (1)基本介紹 該操做符經過傳入一個函數閉包把原來的 Observable 序列轉變爲一個新的 Observable 序列。

map

  • 實例 2.7.3
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3)
    .map { $0 * 10}
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 10 20 30

2.7.4 flatMap

  • (1)基本介紹
  1. map 在作轉換的時候容易出現「升維」的狀況。即轉變以後,從一個序列變成了一個序列的序列。
  2. flatMap 操做符會對源 Observable 的每個元素應用一個轉換方法,將他們轉換成 Observables。 而後將這些 Observables 的元素合併以後再發送出來。即又將其 "拍扁"(降維)成一個 Observable 序列。
  3. 這個操做符是很是有用的。好比當 Observable 的元素本生擁有其餘的 Observable 時,咱們能夠將全部子 Observables 的元素髮送出來。

flatMap

  • 實例 2.7.4
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMap { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
複製代碼

運行結果:

image

2.7.5 flatMapLatest

  • (1)基本介紹 flatMapLatest 與 flatMap 的惟一區別是:flatMapLatest 只會接收最新的 value 事件。

flatMapLatest

  • 實例2.7.5 : 這裏咱們將上例中的 flatMap 改成 flatMapLatest
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMapLatest { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
複製代碼

運行結果:

image

2.7.6 flatMapFirst

  • (1)基本介紹
  1. flatMapFirst 與 flatMapLatest 正好相反:flatMapFirst 只會接收最初的 value 事件。
  2. 該操做符能夠防止重複請求: 好比點擊一個按鈕發送一個請求,當該請求完成前,該按鈕點擊都不該該繼續發送請求。即可該使用 flatMapFirst 操做符。

flatMapFirst

  • 實例2.7.6 :這裏咱們將上例中的 flatMapLatest 改成 flatMapFirst。
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .flatMapFirst { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
複製代碼

運行結果:

image

2.7.7 concatMap

  • (1)基本介紹

concatMap 與 flatMap 的惟一區別是:當前一個 Observable 元素髮送完畢後,後一個Observable 才能夠開始發出元素。或者說等待前一個 Observable 產生完成事件後,纔對後一個 Observable 進行訂閱。

concatMap

  • 實例2.7.7
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .concatMap { $0 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
variable.value = subject2
subject2.onNext("2")
subject1.onNext("C")
subject1.onCompleted() //只有前一個序列結束後,才能接收下一個序列
複製代碼

運行結果:

image

2.7.8 scan

  • (1)基本介紹

scan 就是先給一個初始化的數,而後不斷的拿前一個結果和最新的值進行處理操做。

scan

  • 實例2.7.8
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4, 5)
    .scan(0) { acum, elem in
        acum + elem
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果

image

2.7.9 groupBy

  • (1)基本介紹

groupBy 操做符將源 Observable 分解爲多個子 Observable,而後將這些子 Observable 發送出來。 也就是說該操做符會將元素經過某個鍵進行分組,而後將分組後的元素序列以 Observable 的形態發送出來。

groupBy

  • 實例2.7.9
let disposeBag = DisposeBag()
 
//將奇數偶數分紅兩組
Observable<Int>.of(0, 1, 2, 3, 4, 5)
    .groupBy(keySelector: { (element) -> String in
        return element % 2 == 0 ? "偶數" : "基數"
    })
    .subscribe { (event) in
        switch event {
        case .next(let group):
            group.asObservable().subscribe({ (event) in
                print("key:\(group.key) event:\(event)")
            })
            .disposed(by: disposeBag)
        default:
            print("")
        }
    }
.disposed(by: disposeBag)
複製代碼

運行結果:

image

2.8 過濾操做符:filter、take、skip等

  • 過濾操做指的是從源 Observable 中選擇特定的數據發送。

2.8.1 filter

  • (1)基本介紹

該操做符就是用來過濾掉某些不符合要求的事件。

filter

  • 實例2.8.1
let disposeBag = DisposeBag()
 
Observable.of(2, 30, 22, 5, 60, 3, 40 ,9)
    .filter {
        $0 > 10
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

image

2.8.2 distinctUntilChanged

  • 該操做符用於過濾掉連續重複的事件。
    distinctUntilChanged
  • 實例2.8.2
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 1, 1, 4)
    .distinctUntilChanged()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果:

image

2.8.3 single

  • (1)基本介紹

限制只發送一次事件,或者知足條件的第一個事件。 若是存在有多個事件或者沒有事件都會發出一個 error 事件。 若是隻有一個事件,則不會發出 error 事件。

single

  • 實例2.8.3
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .single{ $0 == 2 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
Observable.of("A", "B", "C", "D")
    .single()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果:

image

2.8.4 elementAt

  • 該方法實現只處理在指定位置的事件。
    elementAt
  • 實例2.8.4
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .elementAt(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 3

2.8.5 ignoreElements

  • (1)基本介紹

該操做符能夠忽略掉全部的元素,只發出 error 或 completed 事件。 若是咱們並不關心 Observable 的任何元素,只想知道 Observable 在何時終止,那就可使用 ignoreElements 操做符。

ignoreElements

  • 實例2.8.5
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .ignoreElements()
    .subscribe{
        print($0)
    }
    .disposed(by: disposeBag)
複製代碼

運行結果: completed

2.8.6 take

  • 該方法實現僅發送 Observable 序列中的前 n 個事件,在知足數量以後會自動 .completed。
    take
  • 實例2.8.6
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .take(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 1 2

2.8.7 takeLast

  • 該方法實現僅發送 Observable 序列中的後 n 個事件。
    takeLast
  • 實例2.8.7
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .takeLast(1)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 4

2.8.8 skip

  • 該方法用於跳過源 Observable 序列發出的前 n 個事件。
    skip
  • 實例 2.8.8
let disposeBag = DisposeBag()
 
Observable.of(1, 2, 3, 4)
    .skip(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 3 4

2.8.9 Sample

  1. Sample 除了訂閱源 Observable 外,還能夠監視另一個 Observable, 即 notifier 。
  2. 每當收到 notifier 事件,就會從源序列取一個最新的事件併發送。而若是兩次 notifier 事件之間沒有源序列的事件,則不發送值。

Sample

  • 實例2.8.9
let disposeBag = DisposeBag()
 
let source = PublishSubject<Int>()
let notifier = PublishSubject<String>()
 
source
    .sample(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext(1)
 
//讓源序列接收接收消息
notifier.onNext("A")
 
source.onNext(2)
 
//讓源序列接收接收消息
notifier.onNext("B")
notifier.onNext("C")
 
source.onNext(3)
source.onNext(4)
 
//讓源序列接收接收消息
notifier.onNext("D")
 
source.onNext(5)
 
//讓源序列接收接收消息
notifier.onCompleted()
複製代碼

運行結果:

1 2 4 5

2.8.10 debounce

  • (1)基本介紹
  1. debounce 操做符能夠用來過濾掉高頻產生的元素,它只會發出這種元素:該元素產生後,一段時間內沒有新元素產生。
  2. 換句話說就是,隊列中的元素若是和下一個元素的間隔小於了指定的時間間隔,那麼這個元素將被過濾掉。
  3. debounce 經常使用在用戶輸入的時候,不須要每一個字母敲進去都發送一個事件,而是稍等一下取最後一個事件。

debounce

  • 實例2.8.10:
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
 
        //定義好每一個事件裏的值以及發送的時間
        let times = [
            [ "value": 1, "time": 0.1 ],
            [ "value": 2, "time": 1.1 ],
            [ "value": 3, "time": 1.2 ],
            [ "value": 4, "time": 1.2 ],
            [ "value": 5, "time": 1.4 ],
            [ "value": 6, "time": 2.1 ]
        ]
         
        //生成對應的 Observable 序列並訂閱
        Observable.from(times)
            .flatMap { item in
                return Observable.of(Int(item["value"]!))
                    .delaySubscription(Double(item["time"]!),
                                       scheduler: MainScheduler.instance)
            }
            .debounce(0.5, scheduler: MainScheduler.instance) //只發出與下一個間隔超過0.5秒的元素
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
    }
}
複製代碼

運行結果: 1 5 6

2.9 條件和布爾操做符:amb、takeWhile、skipWhile等

  • 條件和布爾操做會根據條件發射或變換 Observables,或者對他們作布爾運算。

2.9.1 amb

  • 當傳入多個 Observablesamb 操做符時,它將取第一個發出元素或產生事件的 Observable,而後只發出它的元素。並忽略掉其餘的 Observables

    amb

  • 實例 2.9.1

let disposeBag = DisposeBag()
 
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
let subject3 = PublishSubject<Int>()
 
subject1
    .amb(subject2)
    .amb(subject3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject2.onNext(1)
subject1.onNext(20)
subject2.onNext(2)
subject1.onNext(40)
subject3.onNext(0)
subject2.onNext(3)
subject1.onNext(60)
subject3.onNext(0)
subject3.onNext(0)
複製代碼

運行結果: 1 2 3

2.9.2 takeWhile

  • 該方法依次判斷 Observable 序列的每個值是否知足給定的條件。 當第一個不知足條件的值出現時,它便自動完成。

    takeWhile

  • 實例 2.9.2

let disposeBag = DisposeBag()
 
Observable.of(2, 3, 4, 5, 6)
    .takeWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果: 2 3

2.9.3 takeUntil

  • 基本介紹
  1. 除了訂閱源 Observable 外,經過 takeUntil 方法咱們還能夠監視另一個 Observable, 即 notifier
  2. 若是 notifier 發出值或 complete 通知,那麼源 Observable 便自動完成,中止發送事件。
    takeUntil
  • 實例 2.9.3
let disposeBag = DisposeBag()
 
let source = PublishSubject<String>()
let notifier = PublishSubject<String>()
 
source
    .takeUntil(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext("a")
source.onNext("b")
source.onNext("c")
source.onNext("d")
 
//中止接收消息
notifier.onNext("z")
 
source.onNext("e")
source.onNext("f")
source.onNext("g")
複製代碼

輸出結果: a b c d

2.9.4 skipWhile

  • 基本介紹
  1. 該方法用於跳過前面全部知足條件的事件。
  2. 一旦遇到不知足條件的事件,以後就不會再跳過了。
    skipWhile
  • 實例 2.9.4
let disposeBag = DisposeBag()
 
Observable.of(2, 3, 4, 5, 6)
    .skipWhile { $0 < 4 }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
    }
}
複製代碼

運行結果: 4 5 6

2.9.5 skipUntil

  • 基本介紹
  1. 同上面的 takeUntil 同樣,skipUntil 除了訂閱源 Observable 外,經過 skipUntil 方法咱們還能夠監視另一個 Observable, 即 notifier
  2. takeUntil 相反的是。源 Observable 序列事件默認會一直跳過,直到 notifier 發出值或 complete 通知。

skipUntil

  • 實例 2.9.5
let disposeBag = DisposeBag()
 
let source = PublishSubject<Int>()
let notifier = PublishSubject<Int>()
 
source
    .skipUntil(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
source.onNext(1)
source.onNext(2)
source.onNext(3)
source.onNext(4)
source.onNext(5)
 
//開始接收消息
notifier.onNext(0)
 
source.onNext(6)
source.onNext(7)
source.onNext(8)
 
//仍然接收消息
notifier.onNext(0)
 
source.onNext(9)
複製代碼

運行結果: 6 7 8 9

2.10 結合操做符:startWith、merge、zip等

2.10.1 startWith

  • 基本介紹:

該方法會在 Observable 序列開始以前插入一些事件元素。即發出事件消息以前,會先發出這些預先插入的事件消息

  • 圖解:
    startWiith
  • 實例 2.10.1
let disposeBag = DisposeBag()
         
Observable.of("2", "3")
    .startWith("1")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果:

1 2 3

  • 實例2.10.1.1: 能夠插入多個數據
let disposeBag = DisposeBag()
 
Observable.of("2", "3")
    .startWith("a")
    .startWith("b")
    .startWith("c")
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
複製代碼

運行結果:

c b a 2 3

2.10.2 merge

  • 基本介紹:

該方法能夠將多個(兩個或兩個以上的)Observable 序列合併成一個 Observable 序列。

  • 圖解:
    merge
  • 實例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
 
Observable.of(subject1, subject2)
    .merge()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(20)
subject1.onNext(40)
subject1.onNext(60)
subject2.onNext(1)
subject1.onNext(80)
subject1.onNext(100)
subject2.onNext(1)
複製代碼

運行結果:

20 40 60 1 80 100 1

2.10.3 zip

  • 基本介紹:

該方法能夠將多個(兩個或兩個以上的)Observable 序列壓縮成一個 Observable 序列。 並且它會等到每一個 Observable 事件一一對應地湊齊以後再合併。

  • 圖解:
    zip
  • 實例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<String>()
 
Observable.zip(subject1, subject2) {
    "\($0)\($1)"
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)
複製代碼

運行結果:

1A 2B 3C 4D

  • zip 經常用在整合網絡請求上:

好比咱們想同時發送兩個請求,只有當兩個請求都成功後,再將二者的結果整合起來繼續往下處理。這個功能就能夠經過 zip 來實現。

//第一個請求
let userRequest: Observable<User> = API.getUser("me")
 
//第二個請求
let friendsRequest: Observable<Friends> = API.getFriends("me")
 
//將兩個請求合併處理
Observable.zip(userRequest, friendsRequest) {
        user, friends in
        //將兩個信號合併成一個信號,並壓縮成一個元組返回(兩個信號均成功)
        return (user, friends)
    }
    .observeOn(MainScheduler.instance) //加這個是應爲請求在後臺線程,下面的綁定在前臺線程。
    .subscribe(onNext: { (user, friends) in
        //將數據綁定到界面上
        //.......
    })
    .disposed(by: disposeBag)
複製代碼

2.10.4 combineLatest

  • 基本介紹:
  1. 該方法一樣是將多個(兩個或兩個以上的)Observable 序列元素進行合併。
  2. 但與 zip 不一樣的是,每當任意一個 Observable 有新的事件發出時,它會將每一個 Observable 序列的最新的一個事件元素進行合併。
  • 圖解:
    combineLatest
  • 實例 2.10.1
let disposeBag = DisposeBag()
         
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<String>()
 
Observable.combineLatest(subject1, subject2) {
    "\($0)\($1)"
    }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext(1)
subject2.onNext("A")
subject1.onNext(2)
subject2.onNext("B")
subject2.onNext("C")
subject2.onNext("D")
subject1.onNext(3)
subject1.onNext(4)
subject1.onNext(5)
複製代碼

運行結果:

image

2.10.5 withLatestFrom

  • 基本介紹:

該方法將兩個 Observable 序列合併爲一個。每當 self 隊列發射一個元素時,便從第二個序列中取出最新的一個值。

  • 圖解:

  • 實例 2.10.1

let disposeBag = DisposeBag()
 
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
 
subject1.withLatestFrom(subject2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("A")
subject2.onNext("1")
subject1.onNext("B")
subject1.onNext("C")
subject2.onNext("2")
subject1.onNext("D")
複製代碼

運行結果:

1 1 2

2.10.6 switchLatest

  • 基本介紹:
  1. switchLatest 有點像其餘語言的 switch 方法,能夠對事件流進行轉換。
  2. 好比原本監聽的 subject1,我能夠經過更改 variable 裏面的 value 更換事件源。變成監聽 subject2
  • 圖解:
    switchLatest
  • 實例 2.10.1
let disposeBag = DisposeBag()
 
let subject1 = BehaviorSubject(value: "A")
let subject2 = BehaviorSubject(value: "1")
 
let variable = Variable(subject1)
 
variable.asObservable()
    .switchLatest()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
 
subject1.onNext("B")
subject1.onNext("C")
 
//改變事件源
variable.value = subject2
subject1.onNext("D")
subject2.onNext("2")
 
//改變事件源
variable.value = subject1
subject2.onNext("3")
subject1.onNext("E")
複製代碼

運行結果:

image

2.11

相關文章
相關標籤/搜索