RxSwift (二)序列核心邏輯分析github
RxSwift (三)Observable的建立,訂閱,銷燬編程
RxSwift(四)高階函數json
RxSwift(五)(Rxswift對比swift,oc用法)swift
RxSwift (十) 基礎使用篇 1- 序列,訂閱,銷燬bash
RxSwift學習之十二 (基礎使用篇 3- UI控件擴展)markdown
1)在編寫代碼時咱們常常會須要檢測某些值的變化(好比:textFiled 輸入值的變化、數據請求完成或失敗的變化),而後進行相應的處理。 過去針對不一樣的狀況,咱們須要採用不一樣的事件傳遞方法去處理,好比:delegate、notification、target-action、KVO 等等。 而 RectiveX 機制(由 RxSwift 實現)的出現,讓程序裏的事件傳遞響應方法作到統一。將以前那些經常使用的事件傳遞方法(好比:delegate、notification、target-action 等等),所有替換成 Rx 的「信號鏈」方式。 (2)若是咱們平時使用的是 MVVM 開發模式的話,經過 RxSwift 能夠得到更加方便的數據綁定的方法,使得 MVVM 開發更加如虎添翼。網絡
RxSwift:它只是基於 Swift 語言的 Rx 標準實現接口庫,因此 RxSwift 裏不包含任何 Cocoa 或者 UI 方面的類。 RxCocoa:是基於 RxSwift 針對於 iOS 開發的一個庫,它經過 Extension 的方法給原生的好比 UI 控件添加了 Rx 的特性,使得咱們更容易訂閱和響應這些控件的事件。
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)" } } 複製代碼
import Foundation //歌曲列表數據源 struct MusicListViewModel { let data = [ Music(name: "無條件", singer: "陳奕迅"), Music(name: "你曾是少年", singer: "S.H.E"), Music(name: "從前的我", singer: "陳潔儀"), Music(name: "在木星", singer: "朴樹"), ] } 複製代碼
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])】") } } 複製代碼
ViewModel
作些修改
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: "朴樹"), ]) } 複製代碼
ViewController.swift
)
代碼的簡單說明:
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) } } 複製代碼
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 } 複製代碼
next
事件就是那個能夠攜帶數據 的事件,能夠說它就是一個「最正常」的事件。error
事件表示一個錯誤,它能夠攜帶具體的錯誤內容,一旦 Observable
發出了 error event
,則這個 Observable
就等於終止了,之後它不再會發出 event 事件了。completed
事件表示 Observable
發出的事件正常地結束了,跟 error 同樣,一旦 Observable
發出了 completed event
,則這個 Observable
就等於終止了,之後它不再會發出 event 事件了。Observable
的實例想象成於一個 Swift 中的 Sequence:
Observable
(ObservableType
)至關於一個序列 Sequence
(SequenceType
)。ObservableType.subscribe(_:)
方法其實就至關於 SequenceType.generate()
SequenceType
是同步的循環,而 Observable
是異步的。Observable
對象會在有任何 Event 時候,自動將 Event 做爲一個參數經過 ObservableType.subscribe(_:)
發出,並不須要使用 next 方法。(1)該方法經過傳入一個默認值來初始化。 (2)下面樣例咱們顯式地標註出了 observable 的類型爲 Observable,即指定了這個 Observable 所發出的事件攜帶的數據類型必須是 Int 類型的。
let observable = Observable<Int>.just(5) 複製代碼
(1)該方法能夠接受可變數量的參數(必須要是同類型的) (2)下面樣例中我沒有顯式地聲明出 Observable 的泛型類型,Swift 也會自動推斷類型。
let observable = Observable.of("A", "B", "C") 複製代碼
(1)該方法須要一個數組參數。 (2)下面樣例中數據裏的元素就會被當作這個 Observable 所發出 event 攜帶的數據內容,最終效果同上面餓 of() 樣例是同樣的。
let observable = Observable.from(["A", "B", "C"]) 複製代碼
let observable = Observable<Int>.never() 複製代碼
let observable = Observable<Int>.never() 複製代碼
enum MyError: Error { case A case B } let observable = Observable<Int>.error(MyError.A) 複製代碼
(1)該方法經過指定起始和結束數值,建立一個以這個範圍內全部值做爲初始值的 Observable 序列。 (2)下面樣例中,兩種方法建立的 Observable 序列都是同樣的。
//使用range() let observable = Observable.range(start: 1, count: 5) //使用of() let observable = Observable.of(1, 2, 3 ,4 ,5) 複製代碼
let observable = Observable.repeatElement(1) 複製代碼
(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)該方法接受一個 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)該個方法至關因而建立一個 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 是不同的:
(1)這個方法建立的 Observable 序列每隔一段設定的時間,會發出一個索引數的元素。並且它會一直髮送下去。 (2)下面方法讓其每 1 秒發送一次,而且是在主線程(MainScheduler)發送。
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 複製代碼
//5秒種後發出惟一的一個元素0 let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 複製代碼
//延時5秒種後,每隔1秒鐘發出一個元素 let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 複製代碼
let observable = Observable.of("A", "B", "C") observable.subscribe { event in print(event) } 複製代碼
運行結果以下,能夠看到:
初始化 Observable 序列時設置的默認值都按順序經過 .next 事件發送出來。 當 Observable 序列的初始數據都發送完畢,它還會自動發一個 .completed 事件出來。
let observable = Observable.of("A", "B", "C") observable.subscribe { event in print(event.element) } 複製代碼
運行結果以下:
subscribe
方法,它能夠把 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") }) 複製代碼
運行結果以下:
subscribe()
方法的 onNext
、onError
、onCompleted
和 onDisposed
這四個回調 block 參數都是有默認值的,即它們都是可選的。因此咱們也能夠只處理 onNext
而無論其餘的狀況。let observable = Observable.of("A", "B", "C") observable.subscribe(onNext: { element in print(element) }) 複製代碼
運行結果以下: A B C
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") }) 複製代碼
Observable
序列被建立出來後它不會立刻就開始被激活從而發出 Event,而是要等到它被某我的訂閱了纔會激活它。Observable
序列激活以後要一直等到它發出了 .error
或者 .completed
的 event
後,它才被終結。-(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() 複製代碼
- 咱們能夠把一個 DisposeBag 對象當作一個垃圾袋,把用過的訂閱行爲都放進去。
- 而這個 DisposeBag 就會在本身快要 dealloc 的時候,對它裏面的全部訂閱行爲都調用 dispose() 方法。
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) 複製代碼
(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") }) 複製代碼
運行結果:
//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) 複製代碼
//觀察者 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) 複製代碼
//觀察者 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) } 複製代碼
(1)相較於 AnyObserver 的大而全,Binder 更專一於特定的場景。Binder 主要有如下兩個特徵: 不會處理錯誤事件 確保綁定都是在給定 Scheduler 上執行(默認 MainScheduler) (2)一旦產生錯誤事件,在調試環境下將執行 fatalError,在發佈環境下將打印錯誤信息。
//觀察者 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) 複製代碼
有時咱們想讓 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) } } } 複製代碼
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 已經爲咱們提供許多經常使用的可綁定屬性。好比 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) 複製代碼
當咱們建立一個 Observable 的時候就要預先將要發出的數據都準備好,等到有人訂閱它時再將數據經過 Event 發出去。 但有時咱們但願 Observable 在運行時能動態地「得到」或者說「產生」出一個新的數據,再經過 Event 發送出去。好比:訂閱一個輸入框的輸入內容,當用戶每輸入一個字後,這個輸入框關聯的 Observable 就會發出一個帶有輸入內容的 Event,通知給全部訂閱者。 這個就可使用下面將要介紹的 Subjects 來實現。
-(1)Subjects 既是訂閱者,也是 Observable: 說它是訂閱者,是由於它可以動態地接收新的值。 說它又是一個 Observable,是由於當 Subjects 有了新的值以後,就會經過 Event 將新值發出給他的全部訂閱者。
-(2)一共有四種 Subjects,分別爲:PublishSubject
、BehaviorSubject
、ReplaySubject
、Variable
。他們之間既有各自的特色,也有相同之處:
- 首先他們都是
Observable
,他們的訂閱者都能收到他們發出的新的Event
。- 直到
Subject
發出.complete
或者.error
的Event
後,該Subject
便終結了,同時它也就不會再發出.next
事件。- 對於那些在
Subject
終結後再訂閱他的訂閱者,也能收到subject
發出的一條.complete
或.error
的event
,告訴這個新的訂閱者它已經終結了。- .他們之間最大的區別只是在於:當一個新的訂閱者剛訂閱它的時候,能不能收到
Subject
之前發出過的舊Event
,若是能的話又能收到多少個。
-(3)Subject 經常使用的幾個方法:
onNext(:)
:是 on(.next(:)) 的簡便寫法。該方法至關於 subject 接收到一個 .next 事件。onError(:)
:是 on(.error(:)) 的簡便寫法。該方法至關於 subject 接收到一個 .error 事件。onCompleted()
:是 on(.completed) 的簡便寫法。該方法至關於 subject 接收到一個 .completed 事件。
PublishSubject
是最普通的Subject
,它不須要初始值就能建立。PublishSubject
的訂閱者從他們開始訂閱的時間點起,能夠收到訂閱後Subject
發出的新Event
,而不會收到他們在訂閱前已發出的Event
。
PublishSubject
。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣,能夠發現 PublishSubject
的訂閱者只能收到他們訂閱後的 Event
。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) 複製代碼
運行結果:
-(1)基本介紹
BehaviorSubject
須要經過一個默認初始值來建立。 當一個訂閱者來訂閱它的時候,這個訂閱者會當即收到BehaviorSubjects
上一個發出的event
。以後就跟正常的狀況同樣,它也會接收到BehaviorSubject
以後發出的新的event
。
-(2)時序圖 以下圖:最上面一條是 BehaviorSubject
。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣,能夠發現 BehaviorSubject
的訂閱者一開始就能收到 BehaviorSubjects
以前發出的一個 Event
。
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) 複製代碼
運行結果:
ReplaySubject
在建立時候須要設置一個bufferSize
,表示它對於它發送過的 event 的緩存個數。- 好比一個
ReplaySubject
的bufferSize
設置爲 2,它發出了 3 個.next
的event
,那麼它會將後兩個(最近的兩個)event
給緩存起來。此時若是有一個subscriber
訂閱了這個ReplaySubject
,那麼這個subscriber
就會當即收到前面緩存的兩個.next
的event
。- 若是一個
subscriber
訂閱已經結束的ReplaySubject
,除了會收到緩存的.next
的event
外,還會收到那個終結的.error
或者.complete
的event
。
ReplaySubject
(bufferSize
設爲爲 2)。 下面兩條分別表示兩個新的訂閱,它們訂閱的時間點不一樣。能夠發現 ReplaySubject
的訂閱者一開始就能收到 ReplaySubject
以前發出的兩個 Event
(若是有的話)。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) 複製代碼
運行結果:
-(1)基本介紹
Variable
其實就是對BehaviorSubject
的封裝,因此它也必需要經過一個默認的初始值進行建立。Variable
具備BehaviorSubject
的功能,可以向它的訂閱者發出上一個event
以及以後新建立的event
。- 不一樣的是,
Variable
還把會把當前發出的值保存爲本身的狀態。同時它會在銷燬時自動發送.complete
的event
,不須要也不能手動給Variables
發送completed
或者error
事件來結束它。- 簡單地說就是
Variable
有一個value
屬性,咱們改變這個value
屬性的值就至關於調用通常Subjects
的onNext()
方法,而這個最新的onNext()
的值就被保存在value
屬性裏了,直到咱們再次修改它。Variables
自己沒有subscribe()
方法,可是全部Subjects
都有一個asObservable()
方法。咱們可使用這個方法返回這個Variable
的Observable
類型,拿到這個Observable
類型咱們就能訂閱它了。
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 事件 複製代碼
運行結果:
BehaviorRelay
是做爲Variable
的替代者出現的。它的本質其實也是對BehaviorSubject
的封裝,因此它也必需要經過一個默認的初始值進行建立。BehaviorRelay
具備BehaviorSubject
的功能,可以向它的訂閱者發出上一個event
以及以後新建立的event
。- 與
BehaviorSubject
不一樣的是,不須要也不能手動給BehaviorReply
發送completed
或者error
事件來結束它(BehaviorRelay
會在銷燬時也不會自動發送.complete
的event
)。BehaviorRelay
有一個value
屬性,咱們經過這個屬性能夠獲取最新值。而經過它的accept()
方法能夠對值進行修改。
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") } } 複製代碼
運行結果:
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"]) } } 複製代碼
運行結果:
buffer 方法做用是緩衝組合,第一個參數是緩衝時間,第二個參數是緩衝個數,第三個參數是線程。 該方法簡單來講就是緩存
Observable
中發出的新元素,當元素達到某個數量,或者通過了特定的時間,它就會將這個元素集合發送出來。
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") } } 複製代碼
運行結果:
window 操做符和 buffer 十分類似。不過 buffer 是週期性的將緩存的元素集合發送出來,而 window 週期性的將元素集合以 Observable 的形態發送出來。 同時 buffer 要等到元素蒐集完畢後,纔會發出元素序列。而 window 能夠實時發出元素序列。
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") } } 複製代碼
運行結果:
let disposeBag = DisposeBag() Observable.of(1, 2, 3) .map { $0 * 10} .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果: 10 20 30
map
在作轉換的時候容易出現「升維」的狀況。即轉變以後,從一個序列變成了一個序列的序列。- 而
flatMap
操做符會對源Observable
的每個元素應用一個轉換方法,將他們轉換成Observables
。 而後將這些Observables
的元素合併以後再發送出來。即又將其 "拍扁"(降維)成一個Observable
序列。- 這個操做符是很是有用的。好比當
Observable
的元素本生擁有其餘的Observable
時,咱們能夠將全部子Observables
的元素髮送出來。
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") 複製代碼
運行結果:
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") 複製代碼
運行結果:
- flatMapFirst 與 flatMapLatest 正好相反:flatMapFirst 只會接收最初的 value 事件。
- 該操做符能夠防止重複請求: 好比點擊一個按鈕發送一個請求,當該請求完成前,該按鈕點擊都不該該繼續發送請求。即可該使用 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") 複製代碼
運行結果:
concatMap 與 flatMap 的惟一區別是:當前一個 Observable 元素髮送完畢後,後一個Observable 才能夠開始發出元素。或者說等待前一個 Observable 產生完成事件後,纔對後一個 Observable 進行訂閱。
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() //只有前一個序列結束後,才能接收下一個序列 複製代碼
運行結果:
scan 就是先給一個初始化的數,而後不斷的拿前一個結果和最新的值進行處理操做。
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4, 5) .scan(0) { acum, elem in acum + elem } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果
groupBy 操做符將源 Observable 分解爲多個子 Observable,而後將這些子 Observable 發送出來。 也就是說該操做符會將元素經過某個鍵進行分組,而後將分組後的元素序列以 Observable 的形態發送出來。
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) 複製代碼
運行結果:
該操做符就是用來過濾掉某些不符合要求的事件。
let disposeBag = DisposeBag() Observable.of(2, 30, 22, 5, 60, 3, 40 ,9) .filter { $0 > 10 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 1, 1, 4) .distinctUntilChanged() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果:
限制只發送一次事件,或者知足條件的第一個事件。 若是存在有多個事件或者沒有事件都會發出一個 error 事件。 若是隻有一個事件,則不會發出 error 事件。
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) 複製代碼
運行結果:
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .elementAt(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果: 3
該操做符能夠忽略掉全部的元素,只發出 error 或 completed 事件。 若是咱們並不關心 Observable 的任何元素,只想知道 Observable 在何時終止,那就可使用 ignoreElements 操做符。
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .ignoreElements() .subscribe{ print($0) } .disposed(by: disposeBag) 複製代碼
運行結果: completed
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .take(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果: 1 2
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .takeLast(1) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果: 4
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .skip(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果: 3 4
- Sample 除了訂閱源 Observable 外,還能夠監視另一個 Observable, 即 notifier 。
- 每當收到 notifier 事件,就會從源序列取一個最新的事件併發送。而若是兩次 notifier 事件之間沒有源序列的事件,則不發送值。
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
- debounce 操做符能夠用來過濾掉高頻產生的元素,它只會發出這種元素:該元素產生後,一段時間內沒有新元素產生。
- 換句話說就是,隊列中的元素若是和下一個元素的間隔小於了指定的時間間隔,那麼這個元素將被過濾掉。
- debounce 經常使用在用戶輸入的時候,不須要每一個字母敲進去都發送一個事件,而是稍等一下取最後一個事件。
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
當傳入多個 Observables
到 amb
操做符時,它將取第一個發出元素或產生事件的 Observable
,而後只發出它的元素。並忽略掉其餘的 Observables
。
實例 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
該方法依次判斷 Observable 序列的每個值是否知足給定的條件。 當第一個不知足條件的值出現時,它便自動完成。
實例 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
- 除了訂閱源
Observable
外,經過takeUntil
方法咱們還能夠監視另一個Observable
, 即notifier
。- 若是
notifier
發出值或complete
通知,那麼源Observable
便自動完成,中止發送事件。![]()
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
- 該方法用於跳過前面全部知足條件的事件。
- 一旦遇到不知足條件的事件,以後就不會再跳過了。
![]()
let disposeBag = DisposeBag() Observable.of(2, 3, 4, 5, 6) .skipWhile { $0 < 4 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) } } 複製代碼
運行結果: 4 5 6
- 同上面的
takeUntil
同樣,skipUntil
除了訂閱源Observable
外,經過skipUntil
方法咱們還能夠監視另一個Observable
, 即notifier
。- 與
takeUntil
相反的是。源Observable
序列事件默認會一直跳過,直到notifier
發出值或complete
通知。
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
該方法會在 Observable 序列開始以前插入一些事件元素。即發出事件消息以前,會先發出這些預先插入的事件消息
let disposeBag = DisposeBag() Observable.of("2", "3") .startWith("1") .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 複製代碼
運行結果:
1 2 3
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
該方法能夠將多個(兩個或兩個以上的)
Observable
序列合併成一個Observable
序列。
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
該方法能夠將多個(兩個或兩個以上的)
Observable
序列壓縮成一個Observable
序列。 並且它會等到每一個Observable
事件一一對應地湊齊以後再合併。
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 來實現。
//第一個請求 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) 複製代碼
- 該方法一樣是將多個(兩個或兩個以上的)
Observable
序列元素進行合併。- 但與
zip
不一樣的是,每當任意一個Observable
有新的事件發出時,它會將每一個Observable
序列的最新的一個事件元素進行合併。
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) 複製代碼
運行結果:
該方法將兩個
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
switchLatest
有點像其餘語言的switch
方法,能夠對事件流進行轉換。- 好比原本監聽的
subject1
,我能夠經過更改variable
裏面的value
更換事件源。變成監聽subject2
。
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") 複製代碼
運行結果: