@[TOC](RxSwift學(xué)習(xí)之十 (基礎(chǔ)使用篇 1- 序列稳强,訂閱场仲,銷毀))
- 從 GitHub 上下載最新的代碼:https://github.com/ReactiveX/RxSwift
1. RxSwift簡介
- RxSwift 的作用
1)在編寫代碼時(shí)我們經(jīng)常會(huì)需要檢測(cè)某些值的變化(比如:textFiled 輸入值的變化、數(shù)據(jù)請(qǐng)求完成或失敗的變化)退疫,然后進(jìn)行相應(yīng)的處理渠缕。
過去針對(duì)不同的情況,我們需要采用不同的事件傳遞方法去處理褒繁,比如:delegate亦鳞、notification、target-action棒坏、KVO 等等燕差。
而 RectiveX 機(jī)制(由 RxSwift 實(shí)現(xiàn))的出現(xiàn),讓程序里的事件傳遞響應(yīng)方法做到統(tǒng)一坝冕。將之前那些常用的事件傳遞方法(比如:delegate徒探、notification、target-action 等等)喂窟,全部替換成 Rx 的“信號(hào)鏈”方式测暗。
(2)如果我們平時(shí)使用的是 MVVM 開發(fā)模式的話,通過 RxSwift 可以獲得更加方便的數(shù)據(jù)綁定的方法磨澡,使得 MVVM 開發(fā)更加如虎添翼碗啄。
- RxSwift 與 RxCocoa
RxSwift:它只是基于 Swift 語言的 Rx 標(biāo)準(zhǔn)實(shí)現(xiàn)接口庫,所以 RxSwift 里不包含任何 Cocoa 或者 UI 方面的類钱贯。
RxCocoa:是基于 RxSwift 針對(duì)于 iOS 開發(fā)的一個(gè)庫挫掏,它通過 Extension 的方法給原生的比如 UI 控件添加了 Rx 的特性,使得我們更容易訂閱和響應(yīng)這些控件的事件秩命。
2. RxSwift簡單使用
2.1 響應(yīng)式編程與傳統(tǒng)式編程的比較樣例
- 實(shí)例2.1
-
有這么一個(gè)需求:
表格中顯示的是歌曲信息(歌名尉共,以及歌手)
點(diǎn)擊選中任意一個(gè)單元格,在控制臺(tái)中打印出對(duì)應(yīng)的歌曲信息弃锐。
- 按傳統(tǒng)方式:首先我們創(chuàng)建一個(gè) Music 的結(jié)構(gòu)體袄友,用來保存歌曲名稱、歌手名字霹菊。此外它還遵循 CustomStringConvertible 協(xié)議剧蚣,方便我們輸出調(diào)試支竹。
import UIKit
//歌曲結(jié)構(gòu)體
struct Music {
let name: String //歌名
let singer: String //演唱者
init(name: String, singer: String) {
self.name = name
self.singer = singer
}
}
//實(shí)現(xiàn) CustomStringConvertible 協(xié)議,方便輸出調(diào)試
extension Music: CustomStringConvertible {
var description: String {
return "name:\(name) singer:\(singer)"
}
}
2.1.1 傳統(tǒng)編程
- 首先寫一個(gè) ViewModel
import Foundation
//歌曲列表數(shù)據(jù)源
struct MusicListViewModel {
let data = [
Music(name: "無條件", singer: "陳奕迅"),
Music(name: "你曾是少年", singer: "S.H.E"),
Music(name: "從前的我", singer: "陳潔儀"),
Music(name: "在木星", singer: "樸樹"),
]
}
- 視圖控制器代碼(ViewController.swift)
- 接著我們?cè)O(shè)置 UITableView 的委托鸠按,并讓視圖控制器實(shí)現(xiàn) UITableViewDataSource 和 UITableViewDelegate 協(xié)議礼搁,及相關(guān)的協(xié)議方法。
- 這個(gè)大家肯定都寫過無數(shù)遍了目尖,也沒什么好講的馒吴。算一下,這里一共需要 43 行代碼瑟曲。
import UIKit
import RxSwift
class ViewController: UIViewController {
//tableView對(duì)象
@IBOutlet weak var tableView: UITableView!
//歌曲列表數(shù)據(jù)源
let musicListViewModel = MusicListViewModel()
override func viewDidLoad() {
super.viewDidLoad()
//設(shè)置代理
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController: UITableViewDataSource {
//返回單元格數(shù)量
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return musicListViewModel.data.count
}
//返回對(duì)應(yīng)的單元格
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 {
//單元格點(diǎn)擊
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("你選中的歌曲信息【\(musicListViewModel.data[indexPath.row])】")
}
}
- 下面來看一下Rxswift的編程
2.1.2 Rxswift編程
- 對(duì)
ViewModel
做些修改- 這里我們將 data 屬性變成一個(gè)可觀察序列對(duì)象(
Observable Squence
)饮戳,而對(duì)象當(dāng)中的內(nèi)容和我們之前在數(shù)組當(dāng)中所包含的內(nèi)容是完全一樣的。 - 關(guān)于可觀察序列對(duì)象在后面的文章中我會(huì)詳細(xì)介紹洞拨。簡單說就是“序列”可以對(duì)這些數(shù)值進(jìn)行“訂閱(
Subscribe
)”扯罐,有點(diǎn)類似于“通知(NotificationCenter
)”
- 這里我們將 data 屬性變成一個(gè)可觀察序列對(duì)象(
import RxSwift
//歌曲列表數(shù)據(jù)源
struct MusicListViewModel {
let data = Observable.just([
Music(name: "無條件", singer: "陳奕迅"),
Music(name: "你曾是少年", singer: "S.H.E"),
Music(name: "從前的我", singer: "陳潔儀"),
Music(name: "在木星", singer: "樸樹"),
])
}
- 視圖控制器代碼(
ViewController.swift
)- 這里我們不再需要實(shí)現(xiàn)數(shù)據(jù)源和委托協(xié)議了。而是寫一些響應(yīng)式代碼烦衣,讓它們將數(shù)據(jù)和 UITableView 建立綁定關(guān)系歹河。
- 算了下這里我們只需要 31 行代碼,同之前的相比琉挖,一下減少了 1/4 代碼量启泣。而且代碼也更清爽了些。
代碼的簡單說明:
DisposeBag
:作用是 Rx 在視圖控制器或者其持有者將要銷毀的時(shí)候示辈,自動(dòng)釋法掉綁定在它上面的資源寥茫。它是通過類似“訂閱處置機(jī)制”方式實(shí)現(xiàn)(類似于 NotificationCenter 的 removeObserver)。
rx.items(cellIdentifier:
):這是 Rx 基于 cellForRowAt 數(shù)據(jù)源方法的一個(gè)封裝矾麻。傳統(tǒng)方式中我們還要有個(gè) numberOfRowsInSection 方法纱耻,使用 Rx 后就不再需要了(Rx 已經(jīng)幫我們完成了相關(guān)工作)。
rx.modelSelected
: 這是 Rx 基于 UITableView 委托回調(diào)方法 didSelectRowAt 的一個(gè)封裝险耀。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
//tableView對(duì)象
@IBOutlet weak var tableView: UITableView!
//歌曲列表數(shù)據(jù)源
let musicListViewModel = MusicListViewModel()
//負(fù)責(zé)對(duì)象銷毀
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
//將數(shù)據(jù)源數(shù)據(jù)綁定到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點(diǎn)擊響應(yīng)
tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
print("你選中的歌曲信息【\(music)】")
}).disposed(by: disposeBag)
}
}
2.2 Observable介紹弄喘、創(chuàng)建可觀察序列
Observable 作為 Rx 的根基,我們首先對(duì)它要有一些基本的了解甩牺。
-
Observable<T>:
Observable<T> 這個(gè)類就是 Rx 框架的基礎(chǔ)蘑志,我們可以稱它為可觀察序列。它的作用就是可以異步地產(chǎn)生一系列的 Event(事件)贬派,即一個(gè) Observable<T> 對(duì)象會(huì)隨著時(shí)間推移不定期地發(fā)出 event(element : T) 這樣一個(gè)東西急但。
而且這些 Event 還可以攜帶數(shù)據(jù),它的泛型 <T> 就是用來指定這個(gè) Event 攜帶的數(shù)據(jù)的類型搞乏。
有了可觀察序列波桩,我們還需要有一個(gè) Observer(訂閱者)來訂閱它,這樣這個(gè)訂閱者才能收到 Observable<T> 不時(shí)發(fā)出的 Event请敦。 Event
查看 RxSwift 源碼可以發(fā)現(xiàn)镐躲,事件 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 就是一個(gè)枚舉储玫,也就是說一個(gè) Observable 是可以發(fā)出 3 種不同類型的 Event 事件:
-
next:
next
事件就是那個(gè)可以攜帶數(shù)據(jù) <T> 的事件,可以說它就是一個(gè)“最正秤┰恚”的事件撒穷。 -
error:
error
事件表示一個(gè)錯(cuò)誤,它可以攜帶具體的錯(cuò)誤內(nèi)容敌蚜,一旦Observable
發(fā)出了error event
桥滨,則這個(gè)Observable
就等于終止了窝爪,以后它再也不會(huì)發(fā)出 event 事件了弛车。 -
completed:
completed
事件表示Observable
發(fā)出的事件正常地結(jié)束了,跟 error 一樣蒲每,一旦Observable
發(fā)出了completed event
纷跛,則這個(gè)Observable
就等于終止了,以后它再也不會(huì)發(fā)出 event 事件了邀杏。
-
next:
2.2.1 Observable 與 Sequence比較
- 1)為更好地理解贫奠,我們可以把每一個(gè)
Observable
的實(shí)例想象成于一個(gè) Swift 中的 Sequence:- 即一個(gè)
Observable
(ObservableType
)相當(dāng)于一個(gè)序列Sequence
(SequenceType
)。 -
ObservableType.subscribe(_:)
方法其實(shí)就相當(dāng)于SequenceType.generate()
- 即一個(gè)
- 2)但它們之間還是有許多區(qū)別的:
- Swift 中的
SequenceType
是同步的循環(huán)望蜡,而Observable
是異步的唤崭。 -
Observable
對(duì)象會(huì)在有任何 Event 時(shí)候,自動(dòng)將 Event 作為一個(gè)參數(shù)通過ObservableType.subscribe(_:)
發(fā)出脖律,并不需要使用 next 方法谢肾。
- Swift 中的
2.2.2 創(chuàng)建 Observable 序列
- 我們可以通過如下幾種方法來創(chuàng)建一個(gè) Observable 序列
- just() 方法
(1)該方法通過傳入一個(gè)默認(rèn)值來初始化。
(2)下面樣例我們顯式地標(biāo)注出了 observable 的類型為 Observable<Int>小泉,即指定了這個(gè) Observable 所發(fā)出的事件攜帶的數(shù)據(jù)類型必須是 Int 類型的芦疏。
let observable = Observable<Int>.just(5)
- of() 方法
(1)該方法可以接受可變數(shù)量的參數(shù)(必需要是同類型的)
(2)下面樣例中我沒有顯式地聲明出 Observable 的泛型類型,Swift 也會(huì)自動(dòng)推斷類型微姊。
let observable = Observable.of("A", "B", "C")
- from() 方法
(1)該方法需要一個(gè)數(shù)組參數(shù)酸茴。
(2)下面樣例中數(shù)據(jù)里的元素就會(huì)被當(dāng)做這個(gè) Observable 所發(fā)出 event 攜帶的數(shù)據(jù)內(nèi)容,最終效果同上面餓 of() 樣例是一樣的兢交。
let observable = Observable.from(["A", "B", "C"])
- empty() 方法
該方法創(chuàng)建一個(gè)空內(nèi)容的 Observable 序列薪捍。
let observable = Observable<Int>.never()
- never() 方法
該方法創(chuàng)建一個(gè)永遠(yuǎn)不會(huì)發(fā)出 Event(也不會(huì)終止)的 Observable 序列。
let observable = Observable<Int>.never()
- error() 方法
該方法創(chuàng)建一個(gè)不做任何操作配喳,而是直接發(fā)送一個(gè)錯(cuò)誤的 Observable 序列酪穿。
enum MyError: Error {
case A
case B
}
let observable = Observable<Int>.error(MyError.A)
- range() 方法
(1)該方法通過指定起始和結(jié)束數(shù)值,創(chuàng)建一個(gè)以這個(gè)范圍內(nèi)所有值作為初始值的 Observable 序列界逛。
(2)下面樣例中昆稿,兩種方法創(chuàng)建的 Observable 序列都是一樣的。
//使用range()
let observable = Observable.range(start: 1, count: 5)
//使用of()
let observable = Observable.of(1, 2, 3 ,4 ,5)
- repeatElement() 方法
該方法創(chuàng)建一個(gè)可以無限發(fā)出給定元素的 Event 的 Observable 序列(永不終止)息拜。
let observable = Observable.repeatElement(1)
- generate() 方法
(1)該方法創(chuàng)建一個(gè)只有當(dāng)提供的所有的判斷條件都為 true 的時(shí)候溉潭,才會(huì)給出動(dòng)作的 Observable 序列净响。
(2)下面樣例中,兩種方法創(chuàng)建的 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)
- create() 方法
(1)該方法接受一個(gè) block 形式的參數(shù)馋贤,任務(wù)是對(duì)每一個(gè)過來的訂閱進(jìn)行處理。
(2)下面是一個(gè)簡單的樣例畏陕。為方便演示配乓,這里增加了訂閱相關(guān)代碼
//這個(gè)block有一個(gè)回調(diào)參數(shù)observer就是訂閱這個(gè)Observable對(duì)象的訂閱者
//當(dāng)一個(gè)訂閱者訂閱這個(gè)Observable對(duì)象的時(shí)候,就會(huì)將訂閱者作為參數(shù)傳入這個(gè)block來執(zhí)行一些內(nèi)容
let observable = Observable<String>.create{observer in
//對(duì)訂閱者發(fā)出了.next事件惠毁,且攜帶了一個(gè)數(shù)據(jù)"hangge.com"
observer.onNext("hangge.com")
//對(duì)訂閱者發(fā)出了.completed事件
observer.onCompleted()
//因?yàn)橐粋€(gè)訂閱行為會(huì)有一個(gè)Disposable類型的返回值犹芹,所以在結(jié)尾一定要returen一個(gè)Disposable
return Disposables.create()
}
//訂閱測(cè)試
observable.subscribe {
print($0)
}
- deferred() 方法
(1)該個(gè)方法相當(dāng)于是創(chuàng)建一個(gè) Observable 工廠,通過傳入一個(gè) block 來執(zhí)行延遲 Observable 序列創(chuàng)建的行為鞠绰,而這個(gè) block 里就是真正的實(shí)例化序列對(duì)象的地方腰埂。
(2)下面是一個(gè)簡單的演示樣例:
//用于標(biāo)記是奇數(shù)、還是偶數(shù)
var isOdd = true
//使用deferred()方法延遲Observable序列的初始化蜈膨,通過傳入的block來實(shí)現(xiàn)Observable序列的初始化并且返回屿笼。
let factory : Observable<Int> = Observable.deferred {
//讓每次執(zhí)行這個(gè)block時(shí)候都會(huì)讓奇、偶數(shù)進(jìn)行交替
isOdd = !isOdd
//根據(jù)isOdd參數(shù)翁巍,決定創(chuàng)建并返回的是奇數(shù)Observable驴一、還是偶數(shù)Observable
if isOdd {
return Observable.of(1, 3, 5 ,7)
}else {
return Observable.of(2, 4, 6, 8)
}
}
//第1次訂閱測(cè)試
factory.subscribe { event in
print("\(isOdd)", event)
}
//第2次訂閱測(cè)試
factory.subscribe { event in
print("\(isOdd)", event)
}
運(yùn)行結(jié)果如下,可以看到我們兩次訂閱的得到的 Observable 是不一樣的:
- interval() 方法
(1)這個(gè)方法創(chuàng)建的 Observable 序列每隔一段設(shè)定的時(shí)間灶壶,會(huì)發(fā)出一個(gè)索引數(shù)的元素肝断。而且它會(huì)一直發(fā)送下去。
(2)下面方法讓其每 1 秒發(fā)送一次例朱,并且是在主線程(MainScheduler)發(fā)送孝情。
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
- timer() 方法
- (1) 這個(gè)方法有兩種用法,一種是創(chuàng)建的 Observable 序列在經(jīng)過設(shè)定的一段時(shí)間后洒嗤,產(chǎn)生唯一的一個(gè)元素箫荡。
//5秒種后發(fā)出唯一的一個(gè)元素0
let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
- (2) 另一種是創(chuàng)建的 Observable 序列在經(jīng)過設(shè)定的一段時(shí)間后,每隔一段時(shí)間產(chǎn)生一個(gè)元素渔隶。
//延時(shí)5秒種后羔挡,每隔1秒鐘發(fā)出一個(gè)元素
let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance)
observable.subscribe { event in
print(event)
}
2.3 Observable訂閱、事件監(jiān)聽间唉、訂閱銷毀
2.3.1 Observable訂閱
- 有了 Observable绞灼,我們還要使用 subscribe() 方法來訂閱它,接收它發(fā)出的 Event呈野。
2.3.1.1 第一種訂閱方式
- (1)我們使用 subscribe() 訂閱了一個(gè) Observable 對(duì)象低矮,該方法的 block 的回調(diào)參數(shù)就是被發(fā)出的 event 事件,我們將其直接打印出來被冒。
let observable = Observable.of("A", "B", "C")
observable.subscribe { event in
print(event)
}
運(yùn)行結(jié)果如下军掂,可以看到:
初始化 Observable 序列時(shí)設(shè)置的默認(rèn)值都按順序通過 .next 事件發(fā)送出來轮蜕。
當(dāng) Observable 序列的初始數(shù)據(jù)都發(fā)送完畢,它還會(huì)自動(dòng)發(fā)一個(gè) .completed 事件出來蝗锥。
- (2)如果想要獲取到這個(gè)事件里的數(shù)據(jù)跃洛,可以通過 event.element 得到。
let observable = Observable.of("A", "B", "C")
observable.subscribe { event in
print(event.element)
}
運(yùn)行結(jié)果如下:
2.3.1.2 第二種訂閱方式
- (1)RxSwift 還提供了另一個(gè)
subscribe
方法终议,它可以把 event 進(jìn)行分類:- 通過不同的 block 回調(diào)處理不同類型的 event汇竭。(其中 onDisposed 表示訂閱行為被 dispose 后的回調(diào))
- 同時(shí)會(huì)把 event 攜帶的數(shù)據(jù)直接解包出來作為參數(shù),方便我們使用穴张。
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")
})
運(yùn)行結(jié)果如下:
- (2)
subscribe()
方法的onNext
细燎、onError
、onCompleted
和onDisposed
這四個(gè)回調(diào) block 參數(shù)都是有默認(rèn)值的陆馁,即它們都是可選的找颓。所以我們也可以只處理onNext
而不管其他的情況。
let observable = Observable.of("A", "B", "C")
observable.subscribe(onNext: { element in
print(element)
})
運(yùn)行結(jié)果如下:
A
B
C
2.3.2 事件監(jiān)聽
-
doOn 介紹
(1)我們可以使用 doOn 方法來監(jiān)聽事件的生命周期叮贩,它會(huì)在每一次事件發(fā)送前被調(diào)用。
(2)同時(shí)它和 subscribe 一樣佛析,可以通過不同的 block 回調(diào)處理不同類型的 event益老。比如:
do(onNext:) 方法就是在 subscribe(onNext:) 前調(diào)用
而 do(onCompleted:) 方法則會(huì)在 subscribe(onCompleted:) 前面調(diào)用。 使用樣例
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 從創(chuàng)建到終結(jié)流程
- (1)一個(gè)
Observable
序列被創(chuàng)建出來后它不會(huì)馬上就開始被激活從而發(fā)出 Event寸莫,而是要等到它被某個(gè)人訂閱了才會(huì)激活它捺萌。 - (2)而
Observable
序列激活之后要一直等到它發(fā)出了.error
或者.completed
的event
后,它才被終結(jié)膘茎。
2.3.3.2 dispose() 方法
-(1)使用該方法我們可以手動(dòng)取消一個(gè)訂閱行為桃纯。
-(2)如果我們覺得這個(gè)訂閱結(jié)束了不再需要了,就可以調(diào)用 dispose()
方法把這個(gè)訂閱給銷毀掉披坏,防止內(nèi)存泄漏态坦。
-(3)當(dāng)一個(gè)訂閱行為被 dispose
了,那么之后 observable
如果再發(fā)出 event
棒拂,這個(gè)已經(jīng) dispose
的訂閱就收不到消息了伞梯。下面是一個(gè)簡單的使用樣例。
let observable = Observable.of("A", "B", "C")
//使用subscription常量存儲(chǔ)這個(gè)訂閱方法
let subscription = observable.subscribe { event in
print(event)
}
//調(diào)用這個(gè)訂閱的dispose()方法
subscription.dispose()
2.3.3.13 DisposeBag
- (1)除了 dispose() 方法之外帚屉,我們更經(jīng)常用到的是一個(gè)叫 DisposeBag 的對(duì)象來管理多個(gè)訂閱行為的銷毀:
- 我們可以把一個(gè) DisposeBag 對(duì)象看成一個(gè)垃圾袋谜诫,把用過的訂閱行為都放進(jìn)去。
- 而這個(gè) DisposeBag 就會(huì)在自己快要 dealloc 的時(shí)候攻旦,對(duì)它里面的所有訂閱行為都調(diào)用 dispose() 方法喻旷。
- (2)下面是一個(gè)簡單的使用樣例。
let disposeBag = DisposeBag()
//第1個(gè)Observable牢屋,及其訂閱
let observable1 = Observable.of("A", "B", "C")
observable1.subscribe { event in
print(event)
}.disposed(by: disposeBag)
//第2個(gè)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)的作用就是監(jiān)聽事件,然后對(duì)這個(gè)事件做出響應(yīng)辣之£欤或者說任何響應(yīng)事件的行為都是觀察者。比如:
- 當(dāng)我們點(diǎn)擊按鈕怀估,彈出一個(gè)提示框狮鸭。那么這個(gè)“彈出一個(gè)提示框”就是觀察者 Observer<Void>
- 當(dāng)我們請(qǐng)求一個(gè)遠(yuǎn)程的 json 數(shù)據(jù)后,將其打印出來多搀。那么這個(gè)“打印 json 數(shù)據(jù)”就是觀察者 Observer<JSON>
2.4.2 創(chuàng)建觀察者
2.4.2.1 直接在 subscribe歧蕉、bind 方法中創(chuàng)建觀察者
- 在 subscribe 方法中創(chuàng)建
(1)創(chuàng)建觀察者最直接的方法就是在 Observable 的 subscribe 方法后面描述當(dāng)事件發(fā)生時(shí),需要如何做出響應(yīng)康铭。
(2)比如下面的樣例惯退,觀察者就是由后面的 onNext,onError从藤,onCompleted 這些閉包構(gòu)建出來的催跪。
let observable = Observable.of("A", "B", "C")
observable.subscribe(onNext: { element in
print(element)
}, onError: { error in
print(error)
}, onCompleted: {
print("completed")
})
運(yùn)行結(jié)果:
- 在 bind 方法中創(chuàng)建
(1)下面代碼我們創(chuàng)建一個(gè)定時(shí)生成索引數(shù)的 Observable 序列,并將索引數(shù)不斷顯示在 label 標(biāo)簽上:
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "當(dāng)前索引數(shù):\($0 )"}
.bind { [weak self](text) in
//收到發(fā)出的索引數(shù)后顯示到label上
self?.label.text = text
}
.disposed(by: disposeBag)
2.4.2.2 使用 AnyObserver 創(chuàng)建觀察者
- 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):
//收到發(fā)出的索引數(shù)后顯示到label上
self?.label.text = text
default:
break
}
}
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "當(dāng)前索引數(shù):\($0 )"}
.bind(to: observer)
.disposed(by: disposeBag)
}
2.4.2.3 使用 Binder 創(chuàng)建觀察者
(1)相較于 AnyObserver 的大而全懊蒸,Binder 更專注于特定的場(chǎng)景悔常。Binder 主要有以下兩個(gè)特征:
不會(huì)處理錯(cuò)誤事件
確保綁定都是在給定 Scheduler 上執(zhí)行(默認(rèn) MainScheduler)
(2)一旦產(chǎn)生錯(cuò)誤事件米酬,在調(diào)試環(huán)境下將執(zhí)行 fatalError,在發(fā)布環(huán)境下將打印錯(cuò)誤信息恭取。
- 實(shí)例2.4.2.3
//觀察者
let observer: Binder<String> = Binder(label) { (view, text) in
//收到發(fā)出的索引數(shù)后顯示到label上
view.text = text
}
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "當(dāng)前索引數(shù):\($0 )"}
.bind(to: observer)
.disposed(by: disposeBag)
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { $0 % 2 == 0 }
.bind(to: button.rx.isEnabled)
.disposed(by: disposeBag)
2.5 自定義可綁定屬性
有時(shí)我們想讓 UI 控件創(chuàng)建出來后默認(rèn)就有一些觀察者妒貌,而不必每次都為它們單獨(dú)去創(chuàng)建觀察者通危。比如我們想要讓所有的 UIlabel 都有個(gè) fontSize 可綁定屬性,它會(huì)根據(jù)事件值自動(dòng)改變標(biāo)簽的字體大小灌曙。
通過對(duì) UI 類進(jìn)行擴(kuò)展
這里我們通過對(duì) UILabel 進(jìn)行擴(kuò)展菊碟,增加了一個(gè) fontSize 可綁定屬性。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
//Observable序列(每隔0.5秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
observable
.map { CGFloat($0) }
.bind(to: label.fontSize) //根據(jù)索引數(shù)不斷變放大字體
.disposed(by: disposeBag)
}
}
extension UILabel {
public var fontSize: Binder<CGFloat> {
return Binder(self) { label, fontSize in
label.font = UIFont.systemFont(ofSize: fontSize)
}
}
}
- 通過對(duì) Reactive 類進(jìn)行擴(kuò)展
既然使用了 RxSwift平匈,那么更規(guī)范的寫法應(yīng)該是對(duì) Reactive 進(jìn)行擴(kuò)展框沟。這里同樣是給 UILabel 增加了一個(gè) fontSize 可綁定屬性。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
//Observable序列(每隔0.5秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
observable
.map { CGFloat($0) }
.bind(to: label.rx.fontSize) //根據(jù)索引數(shù)不斷變放大字體
.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 觀察者)
其實(shí) RxSwift 已經(jīng)為我們提供許多常用的可綁定屬性增炭。比如 UILabel 就有 text 和 attributedText 這兩個(gè)可綁定屬性忍燥。
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
}
}
}
那么上文那個(gè)定時(shí)顯示索引數(shù)的樣例,我們其實(shí)不需要自定義 UI 觀察者隙姿,直接使用 RxSwift 提供的綁定屬性即可梅垄。
//Observable序列(每隔1秒鐘發(fā)出一個(gè)索引數(shù))
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
observable
.map { "當(dāng)前索引數(shù):\($0 )"}
.bind(to: label.rx.text) //收到發(fā)出的索引數(shù)后顯示到label上
.disposed(by: disposeBag)
2.6 Subjects、Variables
當(dāng)我們創(chuàng)建一個(gè) Observable 的時(shí)候就要預(yù)先將要發(fā)出的數(shù)據(jù)都準(zhǔn)備好,等到有人訂閱它時(shí)再將數(shù)據(jù)通過 Event 發(fā)出去队丝。
但有時(shí)我們希望 Observable 在運(yùn)行時(shí)能動(dòng)態(tài)地“獲得”或者說“產(chǎn)生”出一個(gè)新的數(shù)據(jù)靡馁,再通過 Event 發(fā)送出去。比如:訂閱一個(gè)輸入框的輸入內(nèi)容机久,當(dāng)用戶每輸入一個(gè)字后臭墨,這個(gè)輸入框關(guān)聯(lián)的 Observable 就會(huì)發(fā)出一個(gè)帶有輸入內(nèi)容的 Event,通知給所有訂閱者膘盖。
這個(gè)就可以使用下面將要介紹的 Subjects 來實(shí)現(xiàn)胧弛。
2.6.1 Subjects
2.6.1.1 Subjects 簡介
-(1)Subjects 既是訂閱者,也是 Observable:
說它是訂閱者侠畔,是因?yàn)樗軌騽?dòng)態(tài)地接收新的值结缚。
說它又是一個(gè) Observable,是因?yàn)楫?dāng) Subjects 有了新的值之后软棺,就會(huì)通過 Event 將新值發(fā)出給他的所有訂閱者红竭。
-(2)一共有四種 Subjects,分別為:PublishSubject
喘落、BehaviorSubject
茵宪、ReplaySubject
、Variable
揖盘。他們之間既有各自的特點(diǎn)眉厨,也有相同之處:
- 首先他們都是
Observable
,他們的訂閱者都能收到他們發(fā)出的新的Event
兽狭。- 直到
Subject
發(fā)出.complete
或者.error
的Event
后,該Subject
便終結(jié)了鹿蜀,同時(shí)它也就不會(huì)再發(fā)出.next
事件箕慧。- 對(duì)于那些在
Subject
終結(jié)后再訂閱他的訂閱者,也能收到subject
發(fā)出的一條.complete
或.error
的event
茴恰,告訴這個(gè)新的訂閱者它已經(jīng)終結(jié)了颠焦。- .他們之間最大的區(qū)別只是在于:當(dāng)一個(gè)新的訂閱者剛訂閱它的時(shí)候,能不能收到
Subject
以前發(fā)出過的舊Event
往枣,如果能的話又能收到多少個(gè)伐庭。
-(3)Subject 常用的幾個(gè)方法:
onNext(:)
:是 on(.next(:)) 的簡便寫法。該方法相當(dāng)于 subject 接收到一個(gè) .next 事件分冈。onError(:)
:是 on(.error(:)) 的簡便寫法圾另。該方法相當(dāng)于 subject 接收到一個(gè) .error 事件。onCompleted()
:是 on(.completed) 的簡便寫法雕沉。該方法相當(dāng)于 subject 接收到一個(gè) .completed 事件集乔。
2.6.1.2 PublishSubject
- (1)基本介紹
PublishSubject
是最普通的Subject
,它不需要初始值就能創(chuàng)建坡椒。
PublishSubject
的訂閱者從他們開始訂閱的時(shí)間點(diǎn)起扰路,可以收到訂閱后Subject
發(fā)出的新Event
尤溜,而不會(huì)收到他們?cè)谟嗛喦耙寻l(fā)出的Event
。
- (2)時(shí)序圖
如下圖:最上面一條是PublishSubject
汗唱。
下面兩條分別表示兩個(gè)新的訂閱宫莱,它們訂閱的時(shí)間點(diǎn)不同,可以發(fā)現(xiàn)PublishSubject
的訂閱者只能收到他們訂閱后的Event
哩罪。
- 實(shí)例 2.6.1.2 :
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)PublishSubject
let subject = PublishSubject<String>()
//由于當(dāng)前沒有任何訂閱者授霸,所以這條信息不會(huì)輸出到控制臺(tái)
subject.onNext("111")
//第1次訂閱subject
subject.subscribe(onNext: { string in
print("第1次訂閱:", string)
}, onCompleted:{
print("第1次訂閱:onCompleted")
}).disposed(by: disposeBag)
//當(dāng)前有1個(gè)訂閱,則該信息會(huì)輸出到控制臺(tái)
subject.onNext("222")
//第2次訂閱subject
subject.subscribe(onNext: { string in
print("第2次訂閱:", string)
}, onCompleted:{
print("第2次訂閱:onCompleted")
}).disposed(by: disposeBag)
//當(dāng)前有2個(gè)訂閱识椰,則該信息會(huì)輸出到控制臺(tái)
subject.onNext("333")
//讓subject結(jié)束
subject.onCompleted()
//subject完成后會(huì)發(fā)出.next事件了绝葡。
subject.onNext("444")
//subject完成后它的所有訂閱(包括結(jié)束后的訂閱),都能收到subject的.completed事件腹鹉,
subject.subscribe(onNext: { string in
print("第3次訂閱:", string)
}, onCompleted:{
print("第3次訂閱:onCompleted")
}).disposed(by: disposeBag)
運(yùn)行結(jié)果:
2.6.1.3 BehaviorSubject
-(1)基本介紹
BehaviorSubject
需要通過一個(gè)默認(rèn)初始值來創(chuàng)建藏畅。
當(dāng)一個(gè)訂閱者來訂閱它的時(shí)候,這個(gè)訂閱者會(huì)立即收到BehaviorSubjects
上一個(gè)發(fā)出的event
功咒。之后就跟正常的情況一樣愉阎,它也會(huì)接收到BehaviorSubject
之后發(fā)出的新的event
。
-(2)時(shí)序圖
如下圖:最上面一條是 BehaviorSubject
力奋。
下面兩條分別表示兩個(gè)新的訂閱榜旦,它們訂閱的時(shí)間點(diǎn)不同,可以發(fā)現(xiàn) BehaviorSubject
的訂閱者一開始就能收到 BehaviorSubjects
之前發(fā)出的一個(gè) Event
景殷。
- 實(shí)例 2.6.1.3
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)BehaviorSubject
let subject = BehaviorSubject(value: "111")
//第1次訂閱subject
subject.subscribe { event in
print("第1次訂閱:", event)
}.disposed(by: disposeBag)
//發(fā)送next事件
subject.onNext("222")
//發(fā)送error事件
subject.onError(NSError(domain: "local", code: 0, userInfo: nil))
//第2次訂閱subject
subject.subscribe { event in
print("第2次訂閱:", event)
}.disposed(by: disposeBag)
運(yùn)行結(jié)果:
2.6.1.4 ReplaySubject
- (1)基本介紹
ReplaySubject
在創(chuàng)建時(shí)候需要設(shè)置一個(gè)bufferSize
溅呢,表示它對(duì)于它發(fā)送過的 event 的緩存?zhèn)€數(shù)。- 比如一個(gè)
ReplaySubject
的bufferSize
設(shè)置為 2猿挚,它發(fā)出了 3 個(gè).next
的event
咐旧,那么它會(huì)將后兩個(gè)(最近的兩個(gè))event
給緩存起來。此時(shí)如果有一個(gè)subscriber
訂閱了這個(gè)ReplaySubject
绩蜻,那么這個(gè)subscriber
就會(huì)立即收到前面緩存的兩個(gè).next
的event
铣墨。- 如果一個(gè)
subscriber
訂閱已經(jīng)結(jié)束的ReplaySubject
,除了會(huì)收到緩存的.next
的event
外办绝,還會(huì)收到那個(gè)終結(jié)的.error
或者.complete
的event
伊约。
- (2)時(shí)序圖
如下圖:最上面一條是ReplaySubject
(bufferSize
設(shè)為為 2)。
下面兩條分別表示兩個(gè)新的訂閱孕蝉,它們訂閱的時(shí)間點(diǎn)不同屡律。可以發(fā)現(xiàn)ReplaySubject
的訂閱者一開始就能收到ReplaySubject
之前發(fā)出的兩個(gè)Event
(如果有的話)昔驱。
- 實(shí)例 2.6.1.4 :
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)bufferSize為2的ReplaySubject
let subject = ReplaySubject<String>.create(bufferSize: 2)
//連續(xù)發(fā)送3個(gè)next事件
subject.onNext("111")
subject.onNext("222")
subject.onNext("333")
//第1次訂閱subject
subject.subscribe { event in
print("第1次訂閱:", event)
}.disposed(by: disposeBag)
//再發(fā)送1個(gè)next事件
subject.onNext("444")
//第2次訂閱subject
subject.subscribe { event in
print("第2次訂閱:", event)
}.disposed(by: disposeBag)
//讓subject結(jié)束
subject.onCompleted()
//第3次訂閱subject
subject.subscribe { event in
print("第3次訂閱:", event)
}.disposed(by: disposeBag)
運(yùn)行結(jié)果:
2.6.2 Variables
- 注意:由于 Variable 在之后版本中將被廢棄疹尾,建議使用 Varible 的地方都改用下面介紹的 BehaviorRelay 作為替代
2.6.2.1 Variable
-(1)基本介紹
Variable
其實(shí)就是對(duì)BehaviorSubject
的封裝,所以它也必須要通過一個(gè)默認(rèn)的初始值進(jìn)行創(chuàng)建。Variable
具有BehaviorSubject
的功能纳本,能夠向它的訂閱者發(fā)出上一個(gè)event
以及之后新創(chuàng)建的event
窍蓝。- 不同的是,
Variable
還把會(huì)把當(dāng)前發(fā)出的值保存為自己的狀態(tài)繁成。同時(shí)它會(huì)在銷毀時(shí)自動(dòng)發(fā)送.complete
的event
吓笙,不需要也不能手動(dòng)給Variables
發(fā)送completed
或者error
事件來結(jié)束它。- 簡單地說就是
Variable
有一個(gè)value
屬性巾腕,我們改變這個(gè)value
屬性的值就相當(dāng)于調(diào)用一般Subjects
的onNext()
方法面睛,而這個(gè)最新的onNext()
的值就被保存在value
屬性里了,直到我們?cè)俅涡薷乃?/li>Variables
本身沒有subscribe()
方法尊搬,但是所有Subjects
都有一個(gè)asObservable()
方法叁鉴。我們可以使用這個(gè)方法返回這個(gè)Variable
的Observable
類型,拿到這個(gè)Observable
類型我們就能訂閱它了佛寿。
- 實(shí)例 2.6.2.1
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)初始值為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 對(duì)象在 viewDidLoad() 方法內(nèi)初始化幌墓,所以它的生命周期就被限制在該方法內(nèi)。當(dāng)這個(gè)方法執(zhí)行完畢后冀泻,這個(gè) Variable 對(duì)象就會(huì)被銷毀常侣,同時(shí)它也就自動(dòng)地向它的所有訂閱者發(fā)出 .completed 事件
運(yùn)行結(jié)果:
2.6.2.2 BehaviorRelay
- 1)基本介紹
BehaviorRelay
是作為Variable
的替代者出現(xiàn)的。它的本質(zhì)其實(shí)也是對(duì)BehaviorSubject
的封裝弹渔,所以它也必須要通過一個(gè)默認(rèn)的初始值進(jìn)行創(chuàng)建胳施。BehaviorRelay
具有BehaviorSubject
的功能,能夠向它的訂閱者發(fā)出上一個(gè)event
以及之后新創(chuàng)建的event
肢专。- 與
BehaviorSubject
不同的是舞肆,不需要也不能手動(dòng)給BehaviorReply
發(fā)送completed
或者error
事件來結(jié)束它(BehaviorRelay
會(huì)在銷毀時(shí)也不會(huì)自動(dòng)發(fā)送.complete
的event
)。BehaviorRelay
有一個(gè)value
屬性博杖,我們通過這個(gè)屬性可以獲取最新值胆绊。而通過它的accept()
方法可以對(duì)值進(jìn)行修改。
- (2)上面的
Variable
樣例我們可以改用成BehaviorRelay
欧募,代碼如下:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)初始值為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")
}
}
運(yùn)行結(jié)果:
- (3)如果想將新值合并到原值上,可以通過
accept()
方法與value
屬性配合來實(shí)現(xiàn)仆抵。(這個(gè)常用在表格上拉加載功能上跟继,BehaviorRelay
用來保存所有加載到的數(shù)據(jù))
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let disposeBag = DisposeBag()
//創(chuàng)建一個(gè)初始值為包含一個(gè)元素的數(shù)組的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"])
}
}
運(yùn)行結(jié)果:
2.7 變換操作符:buffer、map镣丑、flatMap舔糖、scan等
- 變換操作指的是對(duì)原始的 Observable 序列進(jìn)行一些轉(zhuǎn)換,類似于 Swift 中 CollectionType 的各種轉(zhuǎn)換莺匠。
2.7.1 buffer
- (1)基本介紹
buffer 方法作用是緩沖組合金吗,第一個(gè)參數(shù)是緩沖時(shí)間,第二個(gè)參數(shù)是緩沖個(gè)數(shù),第三個(gè)參數(shù)是線程摇庙。
該方法簡單來說就是緩存Observable
中發(fā)出的新元素旱物,當(dāng)元素達(dá)到某個(gè)數(shù)量,或者經(jīng)過了特定的時(shí)間卫袒,它就會(huì)將這個(gè)元素集合發(fā)送出來宵呛。
- 實(shí)例 2.7.1
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
let subject = PublishSubject<String>()
//每緩存3個(gè)元素則組合起來一起發(fā)出。
//如果1秒鐘內(nèi)不夠3個(gè)也會(huì)發(fā)出(有幾個(gè)發(fā)幾個(gè)夕凝,一個(gè)都沒有發(fā)空數(shù)組 [])
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")
}
}
運(yùn)行結(jié)果:
2.7.2 window
- (1)基本介紹
window 操作符和 buffer 十分相似宝穗。不過 buffer 是周期性的將緩存的元素集合發(fā)送出來,而 window 周期性的將元素集合以 Observable 的形態(tài)發(fā)送出來码秉。
同時(shí) buffer 要等到元素搜集完畢后逮矛,才會(huì)發(fā)出元素序列。而 window 可以實(shí)時(shí)發(fā)出元素序列转砖。
- 實(shí)例 2.7.2
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
let subject = PublishSubject<String>()
//每3個(gè)元素作為一個(gè)子Observable發(fā)出须鼎。
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")
}
}
運(yùn)行結(jié)果:
2.7.3 map
- (1)基本介紹
該操作符通過傳入一個(gè)函數(shù)閉包把原來的 Observable 序列轉(zhuǎn)變?yōu)橐粋€(gè)新的 Observable 序列。
- 實(shí)例 2.7.3
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.map { $0 * 10}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
10
20
30
2.7.4 flatMap
- (1)基本介紹
map
在做轉(zhuǎn)換的時(shí)候容易出現(xiàn)“升維”的情況堪藐。即轉(zhuǎn)變之后莉兰,從一個(gè)序列變成了一個(gè)序列的序列。- 而
flatMap
操作符會(huì)對(duì)源Observable
的每一個(gè)元素應(yīng)用一個(gè)轉(zhuǎn)換方法礁竞,將他們轉(zhuǎn)換成Observables
糖荒。 然后將這些Observables
的元素合并之后再發(fā)送出來。即又將其 "拍扁"(降維)成一個(gè)Observable
序列模捂。- 這個(gè)操作符是非常有用的捶朵。比如當(dāng)
Observable
的元素本生擁有其他的Observable
時(shí),我們可以將所有子Observables
的元素發(fā)送出來狂男。
- 實(shí)例 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")
運(yùn)行結(jié)果:
2.7.5 flatMapLatest
- (1)基本介紹
flatMapLatest 與 flatMap 的唯一區(qū)別是:flatMapLatest 只會(huì)接收最新的 value 事件综看。
- 實(shí)例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")
運(yùn)行結(jié)果:
2.7.6 flatMapFirst
- (1)基本介紹
- flatMapFirst 與 flatMapLatest 正好相反:flatMapFirst 只會(huì)接收最初的 value 事件。
- 該操作符可以防止重復(fù)請(qǐng)求:
比如點(diǎn)擊一個(gè)按鈕發(fā)送一個(gè)請(qǐng)求岖食,當(dāng)該請(qǐng)求完成前红碑,該按鈕點(diǎn)擊都不應(yīng)該繼續(xù)發(fā)送請(qǐng)求。便可該使用 flatMapFirst 操作符泡垃。
- 實(shí)例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")
運(yùn)行結(jié)果:
2.7.7 concatMap
- (1)基本介紹
concatMap 與 flatMap 的唯一區(qū)別是:當(dāng)前一個(gè) Observable 元素發(fā)送完畢后,后一個(gè)Observable 才可以開始發(fā)出元素蔑穴≈已埃或者說等待前一個(gè) Observable 產(chǎn)生完成事件后,才對(duì)后一個(gè) Observable 進(jìn)行訂閱存和。
- 實(shí)例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() //只有前一個(gè)序列結(jié)束后奕剃,才能接收下一個(gè)序列
運(yùn)行結(jié)果:
2.7.8 scan
- (1)基本介紹
scan 就是先給一個(gè)初始化的數(shù)衷旅,然后不斷的拿前一個(gè)結(jié)果和最新的值進(jìn)行處理操作。
- 實(shí)例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)
運(yùn)行結(jié)果
2.7.9 groupBy
- (1)基本介紹
groupBy 操作符將源 Observable 分解為多個(gè)子 Observable纵朋,然后將這些子 Observable 發(fā)送出來柿顶。
也就是說該操作符會(huì)將元素通過某個(gè)鍵進(jìn)行分組,然后將分組后的元素序列以 Observable 的形態(tài)發(fā)送出來倡蝙。
- 實(shí)例2.7.9
let disposeBag = DisposeBag()
//將奇數(shù)偶數(shù)分成兩組
Observable<Int>.of(0, 1, 2, 3, 4, 5)
.groupBy(keySelector: { (element) -> String in
return element % 2 == 0 ? "偶數(shù)" : "基數(shù)"
})
.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)
運(yùn)行結(jié)果:
2.8 過濾操作符:filter九串、take、skip等
- 過濾操作指的是從源 Observable 中選擇特定的數(shù)據(jù)發(fā)送寺鸥。
2.8.1 filter
- (1)基本介紹
該操作符就是用來過濾掉某些不符合要求的事件猪钮。
- 實(shí)例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)
2.8.2 distinctUntilChanged
-
該操作符用于過濾掉連續(xù)重復(fù)的事件。
- 實(shí)例2.8.2
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 1, 1, 4)
.distinctUntilChanged()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
2.8.3 single
- (1)基本介紹
限制只發(fā)送一次事件胆建,或者滿足條件的第一個(gè)事件烤低。
如果存在有多個(gè)事件或者沒有事件都會(huì)發(fā)出一個(gè) error 事件。
如果只有一個(gè)事件笆载,則不會(huì)發(fā)出 error 事件扑馁。
- 實(shí)例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)
運(yùn)行結(jié)果:
2.8.4 elementAt
-
該方法實(shí)現(xiàn)只處理在指定位置的事件。
- 實(shí)例2.8.4
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.elementAt(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
3
2.8.5 ignoreElements
- (1)基本介紹
該操作符可以忽略掉所有的元素凉驻,只發(fā)出 error 或 completed 事件腻要。
如果我們并不關(guān)心 Observable 的任何元素,只想知道 Observable 在什么時(shí)候終止涝登,那就可以使用 ignoreElements 操作符雄家。
- 實(shí)例2.8.5
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.ignoreElements()
.subscribe{
print($0)
}
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
completed
2.8.6 take
-
該方法實(shí)現(xiàn)僅發(fā)送 Observable 序列中的前 n 個(gè)事件,在滿足數(shù)量之后會(huì)自動(dòng) .completed胀滚。
- 實(shí)例2.8.6
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.take(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
1
2
2.8.7 takeLast
-
該方法實(shí)現(xiàn)僅發(fā)送 Observable 序列中的后 n 個(gè)事件趟济。
- 實(shí)例2.8.7
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.takeLast(1)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
4
2.8.8 skip
-
該方法用于跳過源 Observable 序列發(fā)出的前 n 個(gè)事件。
- 實(shí)例 2.8.8
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4)
.skip(2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
3
4
2.8.9 Sample
- Sample 除了訂閱源 Observable 外咽笼,還可以監(jiān)視另外一個(gè) Observable顷编, 即 notifier 。
- 每當(dāng)收到 notifier 事件剑刑,就會(huì)從源序列取一個(gè)最新的事件并發(fā)送媳纬。而如果兩次 notifier 事件之間沒有源序列的事件,則不發(fā)送值施掏。
- 實(shí)例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()
運(yùn)行結(jié)果:
1
2
4
5
2.8.10 debounce
- (1)基本介紹
- debounce 操作符可以用來過濾掉高頻產(chǎn)生的元素层宫,它只會(huì)發(fā)出這種元素:該元素產(chǎn)生后,一段時(shí)間內(nèi)沒有新元素產(chǎn)生其监。
- 換句話說就是,隊(duì)列中的元素如果和下一個(gè)元素的間隔小于了指定的時(shí)間間隔限匣,那么這個(gè)元素將被過濾掉抖苦。
- debounce 常用在用戶輸入的時(shí)候毁菱,不需要每個(gè)字母敲進(jìn)去都發(fā)送一個(gè)事件,而是稍等一下取最后一個(gè)事件锌历。
- 實(shí)例2.8.10:
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
//定義好每個(gè)事件里的值以及發(fā)送的時(shí)間
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 ]
]
//生成對(duì)應(yīng)的 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) //只發(fā)出與下一個(gè)間隔超過0.5秒的元素
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
}
運(yùn)行結(jié)果:
1
5
6
2.9 條件和布爾操作符:amb贮庞、takeWhile、skipWhile等
- 條件和布爾操作會(huì)根據(jù)條件發(fā)射或變換 Observables究西,或者對(duì)他們做布爾運(yùn)算窗慎。
2.9.1 amb
-
當(dāng)傳入多個(gè)
Observables
到amb
操作符時(shí),它將取第一個(gè)發(fā)出元素或產(chǎn)生事件的Observable
卤材,然后只發(fā)出它的元素遮斥。并忽略掉其他的Observables
。
實(shí)例 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)
運(yùn)行結(jié)果:
1
2
3
2.9.2 takeWhile
-
該方法依次判斷 Observable 序列的每一個(gè)值是否滿足給定的條件扇丛。 當(dāng)?shù)谝粋€(gè)不滿足條件的值出現(xiàn)時(shí)术吗,它便自動(dòng)完成。
實(shí)例 2.9.2
let disposeBag = DisposeBag()
Observable.of(2, 3, 4, 5, 6)
.takeWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
2
3
2.9.3 takeUntil
- 基本介紹
- 除了訂閱源
Observable
外帆精,通過takeUntil
方法我們還可以監(jiān)視另外一個(gè)Observable
较屿, 即notifier
。- 如果
notifier
發(fā)出值或complete
通知卓练,那么源Observable
便自動(dòng)完成隘蝎,停止發(fā)送事件。
- 實(shí)例 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")
輸出結(jié)果:
a
b
c
d
2.9.4 skipWhile
- 基本介紹
- 該方法用于跳過前面所有滿足條件的事件襟企。
一旦遇到不滿足條件的事件嘱么,之后就不會(huì)再跳過了。
- 實(shí)例 2.9.4
let disposeBag = DisposeBag()
Observable.of(2, 3, 4, 5, 6)
.skipWhile { $0 < 4 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
}
運(yùn)行結(jié)果:
4
5
6
2.9.5 skipUntil
- 基本介紹
- 同上面的
takeUntil
一樣整吆,skipUntil
除了訂閱源Observable
外拱撵,通過skipUntil
方法我們還可以監(jiān)視另外一個(gè)Observable
, 即notifier
表蝙。- 與
takeUntil
相反的是拴测。源Observable
序列事件默認(rèn)會(huì)一直跳過,直到notifier
發(fā)出值或complete
通知府蛇。
- 實(shí)例 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)
運(yùn)行結(jié)果:
6
7
8
9
2.10 結(jié)合操作符:startWith集索、merge、zip等
2.10.1 startWith
- 基本介紹:
該方法會(huì)在 Observable 序列開始之前插入一些事件元素汇跨。即發(fā)出事件消息之前务荆,會(huì)先發(fā)出這些預(yù)先插入的事件消息
-
圖解:
- 實(shí)例 2.10.1
let disposeBag = DisposeBag()
Observable.of("2", "3")
.startWith("1")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
1
2
3
- 實(shí)例2.10.1.1: 可以插入多個(gè)數(shù)據(jù)
let disposeBag = DisposeBag()
Observable.of("2", "3")
.startWith("a")
.startWith("b")
.startWith("c")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
運(yùn)行結(jié)果:
c
b
a
2
3
2.10.2 merge
- 基本介紹:
該方法可以將多個(gè)(兩個(gè)或兩個(gè)以上的)
Observable
序列合并成一個(gè)Observable
序列。
-
圖解:
- 實(shí)例 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)
運(yùn)行結(jié)果:
20
40
60
1
80
100
1
2.10.3 zip
- 基本介紹:
該方法可以將多個(gè)(兩個(gè)或兩個(gè)以上的)
Observable
序列壓縮成一個(gè)Observable
序列穷遂。
而且它會(huì)等到每個(gè)Observable
事件一一對(duì)應(yīng)地湊齊之后再合并函匕。
-
圖解:
- 實(shí)例 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)
運(yùn)行結(jié)果:
1A
2B
3C
4D
- zip 常常用在整合網(wǎng)絡(luò)請(qǐng)求上:
比如我們想同時(shí)發(fā)送兩個(gè)請(qǐng)求,只有當(dāng)兩個(gè)請(qǐng)求都成功后蚪黑,再將兩者的結(jié)果整合起來繼續(xù)往下處理盅惜。這個(gè)功能就可以通過 zip 來實(shí)現(xiàn)中剩。
//第一個(gè)請(qǐng)求
let userRequest: Observable<User> = API.getUser("me")
//第二個(gè)請(qǐng)求
let friendsRequest: Observable<Friends> = API.getFriends("me")
//將兩個(gè)請(qǐng)求合并處理
Observable.zip(userRequest, friendsRequest) {
user, friends in
//將兩個(gè)信號(hào)合并成一個(gè)信號(hào),并壓縮成一個(gè)元組返回(兩個(gè)信號(hào)均成功)
return (user, friends)
}
.observeOn(MainScheduler.instance) //加這個(gè)是應(yīng)為請(qǐng)求在后臺(tái)線程抒寂,下面的綁定在前臺(tái)線程结啼。
.subscribe(onNext: { (user, friends) in
//將數(shù)據(jù)綁定到界面上
//.......
})
.disposed(by: disposeBag)
2.10.4 combineLatest
- 基本介紹:
- 該方法同樣是將多個(gè)(兩個(gè)或兩個(gè)以上的)
Observable
序列元素進(jìn)行合并。- 但與
zip
不同的是屈芜,每當(dāng)任意一個(gè)Observable
有新的事件發(fā)出時(shí)郊愧,它會(huì)將每個(gè)Observable
序列的最新的一個(gè)事件元素進(jìn)行合并。
-
圖解:
- 實(shí)例 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)
運(yùn)行結(jié)果:
2.10.5 withLatestFrom
- 基本介紹:
該方法將兩個(gè)
Observable
序列合并為一個(gè)井佑。每當(dāng)self
隊(duì)列發(fā)射一個(gè)元素時(shí)属铁,便從第二個(gè)序列中取出最新的一個(gè)值。
圖解:
實(shí)例 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")
運(yùn)行結(jié)果:
1
1
2
2.10.6 switchLatest
- 基本介紹:
switchLatest
有點(diǎn)像其他語言的switch
方法毅糟,可以對(duì)事件流進(jìn)行轉(zhuǎn)換红选。- 比如本來監(jiān)聽的
subject1
,我可以通過更改variable
里面的value
更換事件源姆另。變成監(jiān)聽subject2
喇肋。
-
圖解:
- 實(shí)例 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")
運(yùn)行結(jié)果: