如今咱們已經瞭解到了一些基本概念, 就能夠開始使用observables了.git
在這篇教程中, 咱們會編寫幾個建立和訂閱observables的例子, Observable在剛開始使用的時候可能會有一些抽象, 但請放心, 隨着咱們慢慢的深刻, 而且瞭解RxSwift中可用的Observable對象的許多類型, 這些問題都會慢慢迎刃而解的.github
注意: 如轉載本文章, 請聯繫做者, 並給出文章的源地址, 本示例的代碼能夠在Observables下載編程
在本章中, 咱們將使用一個Xcode項目, 該項目已經寫好了CocoaPods所需的東西, 直接打開終端, 去到當前目錄而後輸入:swift
pod install
複製代碼
CocoaPods就會自動的把RxSwift和咱們的工程關聯起來了.vim
注意: 該教程使用的是RxSwift 4.4.1版本, Xcode 10.3, 默認是Swift 5.0版本.數組
Observable是Rx的核心, 咱們將花一些事件討論什麼事Observable, 以及如何建立它們和使用它們.閉包
咱們將會看到**"Observable", "Observable sequence"和"sequence"在Rx中交換使用, 但實際上它們其實都是同樣的. 咱們甚至偶爾會看到一個"stream", 特別是來自RxSwift的開發人員, 他們接觸過不少不一樣的編程環境. "Stream"其實也是指一樣的東西, 可是在RxSwift中, 咱們都稱它爲序列, 而不是"stream", 在RxSwift**中...異步
…或者與序列有關的東西. Observable只是一個序列, 有一些特殊的功能, 其中一個功能——也是最實用的功能——異步(asynchronous). Observable對象在一段時間內產生事件, 這個過程稱爲發出(emitting). Observable的事件能夠包含值, 好比說基礎數據類型, 自定類型的實例, 也能夠是手勢, 好比點擊等等.async
最簡單的表現形式就是使用時序圖, 它只是在時間軸上標記而已.函數
從作到右的箭頭表示事件, 編號的圓圈表示序列的元素. 元素1將被釋放, 通過一段時間以後, 元素2和元素3又會被釋放掉, 若是要問這裏大概須要多少事件.
它能夠是Observable對象生命週期的任何一個點, 接下來咱們就看看Observable對象的聲明週期.
在前面的時序圖中, 咱們能夠看到Observable發出的三個元素, 當一個Observable對象發出一個元素時, 它會在onNext中發出這個元素.
下面是另外一個時序圖, 此次是一個條從頭到結束的Observable事件線.
這個Observable發出了三個tap事件, 最後就結束. 這個過程咱們稱爲completed事件, 由於它已經被終止. 例如咱們可能在監聽一些事件, 而後完成了.
重要的是Observable對象已經被終止了, 不會再次發出任何的東西, 這是正常的結束, 固然, 有時候事情並非咱們想象的那樣順利, 也會有出錯的狀況.
在這個時序圖中發生了一個錯誤, 圖裏用紅色的X表示, Observable發送了一個error的事件, 這與Observable在正常狀況下完成了事件而且終止沒有什麼不一樣, 由於當Observable發出了一個error事件, 那麼它也會終止, 而且不會再發出任何東西.
下面有一個快速的介紹:
以RxSwift的源代碼爲例, 這些事件表示爲枚舉:
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
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
事件包含Swift .Error, .completed
是不包含任何數據的中止事件.
如今咱們已經瞭解了什麼是Observable, 以及它能夠作什麼, 接下來咱們將建立一些Observable來看看它們的行爲.
從當前文章回到咱們的項目工程, 而且替換下面的代碼:
@IBAction func createObservable(_ sender: Any) {
print("--- just, of, from ---")
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable = Observable<Int>.just(one)
}
複製代碼
下面是咱們在上面代碼中所作的:
just的名字和它所作的事情很是的貼切, 由於它就是建立一個只包含單個元素的Observable, 只是Observable的其中一種類型方法, 然而在Rx中, 這種方法被稱爲操做符(Operators).
接下來咱們繼續下一個操做符:
let observable2 = Observable.of(one, two, three)
複製代碼
此次咱們沒有明確的指定類型, 在猜想它有多是**[Int]類型的Observable**對象.
按住option + 鼠標左鍵點擊一下, 咱們能夠到它是一個Int類型的Observable對象, 不是一個數組:
這是由於of操做符能夠接受可變參數, Swift可以根據它推斷出Observable對象的類型.
若是想要建立一個Observable的數組, 咱們能夠簡單的將一個數組傳遞進去, 添加下面的代碼到工程中:
let observable3 = Observable.of([one, two, three])
複製代碼
再次按住option + 鼠標左鍵點擊一下, 咱們就能夠看到它確實是一個**[Int]的Observable對象, just操做符也能夠將數組做爲它的單個元素, 這看起來可能有些奇怪, 然而在just**操做符中, 數組只是單個元素, 不是它的內容.
咱們還能夠用另外一個操做符from來建立Observable對象, 在工程中添加下面代碼:
let observable4 = Observable.from([one, two, three])
複製代碼
from操做符能夠從數組中建立單個的Observable元素, 按住option + 鼠標左鍵點擊一下, 咱們會看到它是一個Int類型的Observable, 而不是**[Int]**, 注意一下, from操做符只接受數組.
如今咱們尚未進行操做, 因此Xcode中的控制檯目前看起來光禿禿的, 除了介紹了上面三個操做符以後, 咱們並無實際操做什麼, 接下來, 咱們就來訂閱一下這些Observable對象, 來點實際的.
做爲一個iOS開發人員, 咱們可能熟悉NotificationCenter, 它會向訂閱者發送通知廣播, 這和RxSwift的Observable對象不一樣. 下面是一個UIKeyboardDidChangeFrame通知的觀察者的例子, 後面有一個處理相關操做的Block:
let observer = NotificationCenter.default.addObserver(
forName: UIResponder.keyboardDidChangeFrameNotification,
object: nil,
queue: nil) { notification in
// Handle receiving notification
}
複製代碼
訂閱一個RxSwift的Observable對象很是的簡單, 咱們用觀察者去訂閱它就能夠了, 就至關於把上面的addObserver()
替換爲subscribe()
, 與NotificationCenter不一樣, Rx中的每一個Observable對象都是不同的, 開發人員一般只會用到它的.default
單例.
更重要的是, 一個Observable對象在沒有訂閱者的狀況下是不會直接就發送事件或者執行任何操做.
記住, Observable對象其實是一個序列的定義, 訂閱一個Observable對象實際上更像在Swift標準庫的迭代器調用next()
.
let sequence = 0..<3
var iterator = sequence.makeIterator()
while let n = iterator.next() {
print(n)
}
/* Prints: 0 1 2 */
複製代碼
不過, 咱們訂閱Observable要比這個更加的精簡, 咱們還能夠爲Observable發出每種事件類型添加處理操做.
回想一下, 一個Observable會發出的.next
, .error
和.completed
事件. .next
事件將發出的元素傳遞給應用程序繼續處理, .error
事件則是一個包含錯誤信息的實例.
來點實際的代碼, 將下面的代碼添加到咱們示例當中:
@IBAction func subscribe(_ sender: Any) {
print("\n--- subscribe ---")
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
}
複製代碼
這和前面的示例相似, 只是此次咱們使用的是of操做符, 如今在這個例子的底部訂閱Observable, 而且在閉包裏添加一段操做代碼:
observable.subscribe { event in
print(event)
}
複製代碼
按住option + 鼠標左鍵點擊一下subscribe
操做符, 咱們會看到它接受一個轉義的block, 該閉包接受Int類型的事件, 而且不返回任何內容, subscribe會返回一個能夠丟棄的閉包, 以管理資源, 稍後咱們將會涉及到Disposable.
觸發事件後, 咱們會看到輸出的結果:
--- subscribe ---
next(1)
next(2)
next(3)
completed
複製代碼
Observable對象爲每一個元素髮出.next
事件, 而後再發出.completed
事件表示終止, 當咱們使用Observable時, 一般會對.next
事件發出的元素比事件自己更感興趣.
那麼咱們如何去訪問呢? 咱們用下面的代碼將以前的訂閱代碼給替換掉:
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
複製代碼
事件是具備元素屬性的, 而且是一個option, 由於.next
事件有一個元素, 只要元素不爲nil, 則可使用option綁定來展開元素, 如今咱們再次觸發, 就會看到控制檯上只打印元素, 並不會打印包含元素的事件, 也不會輸出.completed
事件 .
1
2
3
複製代碼
因爲這種模式使用的很是頻繁, 因此RxSwift爲了讓咱們更加的簡單使用, 還提供了一種操做符, 能夠包含Observable對象發出每一種類型的事件: next, error和completed.
用下面的代碼來代替以前的訂閱代碼:
observable.subscribe(onNext: { element in
print(element)
})
複製代碼
如今, 咱們只須要處理.next
事件的元素, 其餘的內容則會被忽略掉. onNext閉包接收.next
事件的元素做爲參數, 因此咱們不用再像以前那樣子手動的去檢索元素.
如今咱們已經瞭解瞭如何建立一個或者多個元素的Observable對象, 那麼零元素的Observable呢? RxSwift也考慮到了這種狀況, 由於提供了一個.empty
操做符, 它只會發出.completed
事件.
添加下面的例子到咱們的工程中:
@IBAction func empty(_ sender: Any) {
print("\n--- empty ---")
let observable = Observable<Void>.empty()
}
複製代碼
若是咱們沒有辦法去推斷類型, 那麼則必須得顯式的聲明Observable的特定類型, 而empty就是沒有推斷的內容, 因此必須顯式的聲明定義類型.
在這種狀況下咱們使用Void比其餘類型都要好, 將下面的代碼添加到工程中:
observable.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
})
複製代碼
依次來解釋一下:
.next
事件, 就像咱們在前面的示例中那樣..completed
事件不包含任何的元素, 這裏只打印一條消息在控制檯中, 咱們會看到empty只發出.completed
事件:
--- empty ---
Completed
複製代碼
關於這個.empty
有人可能會有疑問, 這個操做符能作什麼? 仔細的想一下, 若是咱們想返回一個當即終止或者有意爲零的Observable元素時, 那麼它就很是的有用了.
與這個empty操做符相反的就是never操做符, 是用來建立一個永遠不會發出任何東西, 也永遠不會終止的Observable對象, 它能夠用來表示無限的持續時間, 把下面的例子添加到工程:
@IBAction func never(_ sender: Any) {
print("\n--- never ---")
let observable = Observable<Any>.never()
observable.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
})
}
複製代碼
一旦咱們出發這個事件, 咱們只會看到標題頭, 而後.next
事件和.completed
事件是不會輸出任何信息的.
到目前爲止, 咱們使用的都是顯式元素或者有值的Observable, 除此以外, 也能夠生成一個範圍的Observable.
把下面的例子添加到工程:
@IBAction func range(_ sender: Any) {
print("\n--- range ---")
// 1
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(
onNext: { i in
// 2
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) / 2.23606).rounded())
print(fibonacci)
})
}
複製代碼
繼續來拆解:
實際上, 有一個比.onNext
更好的處理轉換元素的操做符, 這個咱們會在將來的教程中會學習到.
到目前爲止, 除了never()
示例以外, 咱們一直都在處理Observable對象, 它們會自動的發出.completed
事件並終止Observable序列. 這會使得咱們將重點放在建立和訂閱Observable的機制上, 雖然這樣子很方便, 但會掩蓋了咱們進一步瞭解Observable, 接下來, 咱們細說一下Observable的處理和終止.
有一件事情咱們須要記住, 在Observable對象未接收到訂閱以前, 是不會作任何事情的, 訂閱是用來觸發Observable對象的工做, 而後纔會使Observable發出事件, 直到它發出.error
或.completed
事件並終止爲止, 除此以外, 咱們也能夠經過取消Observable對象的訂閱, 而後終止Observable對象.
把下面的代碼添加到工程中:
@IBAction func dispose(_ sender: Any) {
print("\n--- dispose ---")
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe { event in
// 3
print(event)
}
}
複製代碼
簡單的介紹:
若是咱們要顯式的取消訂閱, 咱們會調用dispose()
. 再次說一下, 取消訂閱或處理完訂閱以後, 當前示例中的Observable將會中止發出事件.
將下面的代碼添加到例子中:
subscription.dispose()
複製代碼
可是這種單獨管理每一個訂閱是很是的繁瑣的, 因此RxSwift提供了一個統一管理的DisposeBag類型, 它可使用添.dispose(by: )
方法將subscribe添加到disposeBag中, 每立即將要釋放Observable對象時, 都會自動的去調用dispose()
方法.
下面的代碼添加到工程中:
@IBAction func disposeBag(_ sender: Any) {
print("\n--- DisposeBag ---")
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe {
// 3
print($0)
}
// 4
.disposed(by: disposeBag)
}
複製代碼
解釋一下代碼的工做流程:
這將會是咱們之後最經常使用的模式, 建立並訂閱一個Observable, 而後當即將subscribe添加到disposeBag中.
爲何要那麼麻煩使用disposeBag?
若是咱們忘了向disposeBag添加訂閱, 或者在訂閱完成以後忘了手動添加dispose, 那麼就會形成本該結束的Observable對象沒有結束, 致使內存泄漏.
幸虧的是, Swift編譯器會提醒咱們沒有使用disposables.
在前面的示例裏, 咱們都是建立Observable對象, 而後再使用.next
來輸出元素, 如今咱們來點不同的, 使用create操做符, 這個操做符容許咱們將指定的Observable對象向訂閱者發出全部事件的另外一種方式.
把下面的示例添加到代碼中:
@IBAction func create(_ sender: Any) {
print("\n--- create ---")
let disposeBag = DisposeBag()
Observable<String>.create { observer in
}
}
複製代碼
create操做符接受一個名爲subscribe的參數, 它工做是提供對Observable調用subscribe的實現, 換一句話說它定義了將發送給訂閱者的全部事件, option + 鼠標左鍵點擊一下:
subscribe參數是一個轉義閉包, 它接受AnyObserver並返回一個逃逸閉包. AnyObserver是一種泛型的類型, 它能夠很是方便的將值添加到一個Observable序列中, 而後將該序列發送給訂閱者.
將create的實現更改成下面的代碼:
Observable<String>.create { observer in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
}
複製代碼
代碼詳情:
.next
事件到Observable, .onNext(_:)
是on(.next(_:))
的快捷方法..completed
事件添加到Observable中, 和上面同樣, .onCompleted
是on(.completed)
的快捷方法..next
事件到Observable.注意: 在最後一步, 咱們返回的是disposable, 這看起來感受會很奇怪, 請記住, subscribe操做符返回一個表示訂閱的
Disposable.create()
則是一個空的disposable, 則不會有其餘的影響, 但有些disposables就另外說了.
這裏提一個問題, 你認爲第二個.onNext("?")
元素會發給訂閱者嗎? 爲何呢?
咱們來試試, 看看你個人猜想是不是正確的, 添加下面的代碼到create實現以後:
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed")}
)
.disposed(by: disposeBag)
複製代碼
如今咱們已經訂閱了Observable, 並實現了全部的處理細節, onNext和onError使用了默認參數**$0輸出元素和錯誤信息, 可是咱們發現結果則是.next
的第一個事件, 接下來就是"Completed"和"Dispose"**.
第二個.next
事件不會打印, 回想一下這其中的緣由咱們也知道, 由於Observable發出了一個.onCompleted
事件而後就終止序列了, 因此後續的.onNext
事件則不會再發出.
--- create ---
1
Completed
Disposed
複製代碼
若是咱們向Observable添加錯誤會發生什麼樣的事情呢? 在示例的頂部添加下面的代碼:
enum MyError: Error {
case anError
}
複製代碼
接下來咱們在create中將observer.onCompleted
替換成observer.onError
:
observer.onError(MyError.anError)
複製代碼
Observable則會發出錯誤信息, 而後終止序列:
--- create with error ---
1
anError
Disposed
複製代碼
若是咱們既沒有發出.completed
, .error
事件, 也沒有將subscribe添加到disposeBag中, 那麼會發生什麼狀況呢?
下面是完整的實現:
@IBAction func createWithMemoryLeak(_ sender: Any) {
enum MyError: Error {
case anError
}
print("\n--- create with memory leak ---")
let disposeBag = DisposeBag()
Observable<String>.create { observer in
observer.onNext("1")
observer.onNext("?")
return Disposables.create()
}
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed")}
)
}
複製代碼
恭喜你, 你剛剛建立了一個無限的Observable序列, 該Observable永遠都不會結束.
--- create with memory leak ---
1
?
複製代碼
若是你無法忍受這個示例處於內容泄漏狀態, 能夠把剛剛咱們刪掉的.completed
, .error
事件添加回去, 而且將subscribe添加到disposeBag中.
與其建立一個等待訂閱者的Observable對象, 不如建立一個Observable工廠, 爲每一個訂閱者提供一個新的Observable對象.
把這個新例子添加到工程中:
@IBAction func deferred(_ sender: Any) {
let disposeBag = DisposeBag()
print("\n--- deferred ---")
// 1
var flip = false
// 2
let factory: Observable<Int> = Observable.deferred {
// 3
flip.toggle()
// 4
if flip {
return Observable.of(1, 2, 3)
} else {
return Observable.of(4, 5, 6)
}
}
}
複製代碼
解釋一下代碼:
從外部看, 一個Observable對象工廠和普通的Observable沒啥區別, 添加下面的代碼到示例中:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
複製代碼
每訂閱一次factory就會獲得相反的Observable的value, 先獲得123, 而後再獲得456, 每有一個新的訂閱都會重複這個模式:
--- deferred ---
123
456
123
456
複製代碼
Observable的特性, 要比常規的Observable行爲範圍更小, 它們的使用是可選的, 咱們能夠在任何一個使用Observable特性的地方使用一個常規的Observable對象, 它們的目的是提供一種方式來更清楚的向代碼閱讀者或者是API的使用者傳達咱們的意圖, 使用Observable的特性, 可使得咱們的代碼更加的直觀.
RxSwift有三種特性: Single, Maybe和Completable, 在不瞭解它們的狀況下, 你能猜到它們是作什麼的嗎?
Singles只會發出.success(value)
或.error
事件, .success(value)
其實是.next
和.completed
事件的組合, 這對於使用一次性進程很是有用, 一次性進程要麼成功輸出一個value, 要麼就失敗, 例以下載數據或從硬盤加載數據. Completable只會發出.completed
或.error
事件, 它不會發出任何的值, 只關於某個操做成功完成或者是失敗了, 例如文件的寫入, 咱們就可使用completable.
最後, Maybe是Single和Completable的組合, 它能夠發出.success(value)
, .completed
或.error
事件, 若是咱們須要實現一個可能會成功或者失敗的操做, 而且在成功的時候返回一個值, 那麼Maybe就是咱們所須要的...
咱們會在將來的教程中學習更多有關於RxSwift的特性, 如今咱們來運行一個基本的示例, 在工程中, 咱們有一個hello.text文件, 咱們將使用Observable的特性來加載並輸出這個文本內容.
把下面的例子添加到咱們的工程中:
// 1
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}
// 2
private func loadText(from name: String) -> Single<String> {
// 3
return Single.create { single in
}
}
複製代碼
解釋一下咱們要在代碼中作的事情:
在create閉包中添加下面的代碼來實現咱們須要的操做:
// 1.
let disposable = Disposables.create ()
// 2
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}
// 3
guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}
// 4
guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}
// 5
single(.success(contents))
return disposable
複製代碼
咱們解釋一下這段代碼:
.fileNotFound
和disposable.unreadable
和disposable..encodingFailed
和disposable如今咱們能夠來使用這個函數了, 將下面的代碼添加到工程中:
// 1
loadText(from: "hello")
// 2
.subscribe {
// 3
switch $0 {
case .success(let string):
print(string)
case .error(let error):
print(error)
}
}
.disposed(by: disposeBag)
複製代碼
在這裏咱們作的操做是:
loadText(from:)
, 而後傳遞你要讀取的文件名.如今咱們應該能夠看到控制檯中輸出的文本內容:
--- single ---
Hello RxSwift
複製代碼
若是咱們將文件名換成其餘的話, 應該會打印出未找到文件的錯誤信息.
剩下Completable和Maybe這裏就不作介紹了, 工程中有對應的代碼:
熟能生巧, 咱們將實踐在本章中所學到的只是來完成這些挑戰, 而且得到更多關於使用Observable對象的知識.
在前面的never操做符實例中, 沒有輸出任何的內容, 那是由於咱們輸出的內容是添加到subscribe之中, 若是咱們能添加到在這以前, 而且再將subscribe添加到disposeBag, 那咱們就能夠看到消息的輸出了.
還有一個很是實用的操做符, 它容許咱們在不印象Observable對象的狀況額外添加一些咱們想作的事情.
do操做符將容許咱們添加附加效果, 也就是說, 無論這個Observable對象會發出什麼樣的事件, do只是將事件傳遞連接到下一個操做符中, do這個操做符還包含一個onSubscribe的閉包, 咱們將會在這裏處理額外的事情.
使用do操做符的方法是do(onNext:onError:onCompleted:onSubscribe:onDispose)
, 你能夠在這裏實現任何你想作的事情.
咱們將使用do操做符改造一下咱們的never示例, 在改造的時候, 千萬別忘了把subscribe添加到disposeBag中.
除了剛剛咱們提過的do操做符能夠做爲Rx的調試方式以外, 還有一種操做符叫作debug操做符, 它將爲一個Observable對象打印出每一個事件信息.
它有幾個可選參數, 可能最有用的就是包含一個標識符字符串, 該字符串會打印出來, 在複雜的Rx鏈中, 咱們能夠在多個位置添加debug操做符, 這能夠幫助咱們區分打印出來的源subscribe.
經過使用debug操做符替換do操做符, 併爲其提供一個字符串做爲標識符.
1.當Observable事件完成時會發出Completed事件, 表明事件結束;
2.當Observable在未完成事件前發生了錯誤, 該事件也會結束, 而且拋出error事件;
3.當Observable的事件完成了以後就不會再發出任何事件了.
subscribe()
函數訂閱Observable.create()
操做符建立的Observable, 在處理完事件以後, 必定要調用一下.onCompleted()
或者是.onError()
, 而且在subscribe時必定要調用disposed(by: DisposeBag)
, 不然Observable永遠不會被釋放, 形成內存泄漏..success(value)
或.error
事件, 而.success(value)
其實是.next
和.completed
事件的組合.completed
或.error
事件, 不會發出任何值.success(value)
, .completed
或者.error
事件