一.Observable
- 可監(jiān)聽序列
1.特征序列Driver
<1>表示具有以下特征的可觀察序列:
- 不會產(chǎn)生
error
事件 - 在
MainScheduler
上監(jiān)聽事件 - 使用了
share(replay: 1, scope: .whileConnected)
共享策略(共享附加作用)
這些都是驅(qū)動UI
的序列所需要具有的特征带膀。
<2>補充說明:
- 所有訂閱者共享序列計算資源
- 如果可觀察序列產(chǎn)生了至少一個元素,則在進行新訂閱后,最后一個產(chǎn)生的元素將立即在訂閱的同一線程上重播
- 如果沒有訂閱者芽隆,它將釋放序列計算資源
<3>使用場景:
- 通過
CoreData
模型驅(qū)動UI
- 使用一個
UI
元素值來驅(qū)動另外一個UI
元素值 - 搜索框中每次輸入一個文本努咐,就會獲取一次網(wǎng)絡請求显蝌,請求成功后將數(shù)據(jù)渲染到
UI
界面
2.特征序列ControlEvent
專門用于描述UI控件所產(chǎn)生的事件当叭,具有以下特征:
- 不會產(chǎn)生
error
事件 - 一定在
MainScheduler
訂閱(主線程訂閱) - 一定在
MainScheduler
監(jiān)聽(主線程監(jiān)聽) - 共享附加作用
二.Observer
- 觀察者
1. AnyObserver
:可以描述任意一種觀察者。
用戶名提示語是否隱藏:
usernameValid
.bind(to: usernameValidLabel.rx.isHidden)
.disposed(by: disposeBag)
可以看作是:
let observer: AnyObserver<Bool> = AnyObserver { [weak self] event in
switch event {
case .next(let isHidden):
self?.usernameValidLabel.isHidden = isHidden
default:
break
}
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
2.特征觀察者Binder
<1>表示具有以下特征的觀察者:
- 不會處理
error
事件 - 確保綁定都是在給定
Scheduler
上執(zhí)行(默認MainScheduler
)
<2>在上面的AnyObserver
例子昙楚,由于這個觀察者是一個UI觀察者近速,所以它在響應事件時诈嘿,只會處理next
事件,并且更新UI
的操作需要在主線程上執(zhí)行削葱,因此更好的方案就是使用Binder
:
let observer: Binder<Bool> = Binder(usernameValidLabel) { label, isHidden in
label.isHidden = isHidden
}
usernameValid
.bind(to: observer)
.disposed(by: disposeBag)
<3>由于一個頁面是否隱藏是一個常用的觀察者奖亚,所以應該讓所有的UIView
都提供這種觀察者:
extension Reactive where Base: UIView {
public var isHidden: Binder<Bool> {
return Binder(self.base) { view, hidden in
view.isHidden = hidden
}
}
}
usernameValid
.bind(to: usernameValidLabel.rx.isHidden)
.disposed(by: disposeBag)
三.Observable & Observer
- 即是可監(jiān)聽序列也是觀察者
1.PublishSubject
:將對觀察者發(fā)送訂閱后產(chǎn)生的元素,而在訂閱前發(fā)出的元素將不會發(fā)送給觀察者析砸。
let subject = PublishSubject<String>()
subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("??")
subject.onNext("??")
subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("???")
subject.onNext("???")
/// 輸出結(jié)果:第2個觀察者不會收到訂閱前發(fā)出的元素??和??
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
2.ReplaySubject
:在新觀察者訂閱時昔字,補發(fā)已經(jīng)發(fā)送過的元素。
這里存在多個版本的ReplaySubject
首繁,有的只會將最新的n個元素發(fā)送給觀察者作郭,有的只會將限制時間段內(nèi)的最新元素發(fā)送給觀察者。
如果ReplaySubject
當作觀察者來使用弦疮,注意不要多個線程調(diào)用onNext
夹攒、onError
或onCompleted
。這樣會導致無序調(diào)用胁塞,將造成意想不到的結(jié)果咏尝。
let subject = ReplaySubject<String>.create(bufferSize: 1)
subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("??")
subject.onNext("??")
subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("???")
subject.onNext("???")
/// 輸出結(jié)果:當?shù)?個觀察者訂閱的時候,會發(fā)送已經(jīng)發(fā)送過的最后一個元素(bufferSize為1)啸罢。
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
3.BehaviorSubject
:在新觀察者訂閱時编检,會發(fā)送最近發(fā)送的一個元素,如果沒有扰才,則發(fā)送一個默認元素值允懂。
let subject = BehaviorSubject(value: "??")
subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("??")
subject.onNext("??")
subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("???")
subject.onNext("???")
/// 輸出結(jié)果:第1個觀察者訂閱時,沒有發(fā)送過的元素衩匣,發(fā)送默認元素??
/// 第2個觀察者訂閱時蕾总,發(fā)送最近發(fā)送過的元素??
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 1 Event: next(??)
Subscription: 2 Event: next(??)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
4.AsyncSubject
:在源Observable
產(chǎn)生完成事件(onCompleted
)后,發(fā)送最后一個元素(僅僅只有最后一個元素)舵揭,如果源 Observable
沒有發(fā)出任何元素谤专,只有一個完成事件,那么AsyncSubject
也只有一個完成事件午绳。
let subject = AsyncSubject<String>()
subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("??")
subject.onNext("??")
subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)
subject.onNext("???")
subject.onNext("???")
subject.onCompleted()
/// 輸出結(jié)果:只會發(fā)送最后一個元素???
Subscription: 1 Event: next(???)
Subscription: 2 Event: next(???)
Subscription: 1 Event: completed
Subscription: 2 Event: completed
5.ControlProperty
專門用于描述UI控件屬性的,具有以下特征:
- 不會產(chǎn)生
error
事件 - 一定在
MainScheduler
訂閱(主線程訂閱) - 一定在
MainScheduler
監(jiān)聽(主線程監(jiān)聽) - 共享附加作用
四.Error Handing
- 錯誤處理
一旦序列里面產(chǎn)生了一個error
事件映之,整個序列將被終止拦焚,RxSwift有兩種錯誤處理機制:
1.catch
:恢復
-
catchAndReturn(_:)
:當錯誤產(chǎn)生時,返回一個指定的元素杠输,從而使該可觀察序列繼續(xù)赎败。 -
catch(_:)
:當錯誤產(chǎn)生時,將錯誤事件替換成一個備選序列蠢甲,從而使該可觀察序列繼續(xù)僵刮。
2.retry
:重試
-
retry(_)
:讓序列在發(fā)生錯誤時,進行指定次數(shù)的重試操作。 -
retry(when:)
:讓序列在發(fā)生錯誤時搞糕,延時一段時間后進行重試操作勇吊。
二.DelegateProxy
-
DelegateProxy
是代理委托,可以將它看作是代理的代理窍仰。 -
DelegateProxy
的作用是作為一個中間代理汉规,它會把系統(tǒng)的delegate
對象保存一份,然后攔截delegate
方法驹吮。也就是說在每次觸發(fā)delegate
方法之前针史,會先調(diào)用DelegateProxy
對應的方法,可以在方法里面發(fā)送序列給多個訂閱者碟狞。
三.創(chuàng)建Observables
1.deferred
:直到訂閱發(fā)生啄枕,才創(chuàng)建Observable
,并為每個訂閱者創(chuàng)建一個新的Observable
族沃。
通常帶有一個Observable
工廠函數(shù)频祝,為每個訂閱者重新執(zhí)行此操作,每個訂閱者都有自己的單獨序列竭业。
2.just
:創(chuàng)建只能發(fā)出唯一一個元素的Observable
智润。
一個序列只有唯一的元素0:
let id = Observable.just(0)
它相當于:
let id = Observable<Int>.create { observer in
observer.onNext(0)
observer.onCompleted()
return Disposables.create()
}
3.using
:通過使用using
操作符創(chuàng)建Observable
時,同時創(chuàng)建一個可被清除的資源(Disposable)未辆,一旦Observable
終止了窟绷,那么這個資源就會被清除掉了。
四.組合Observables
1.combineLatest
:將多個Observable
合并咐柜,當任意一個Observable
發(fā)出元素時兼蜈,通過指定的函數(shù)組合每個Observable
發(fā)出的最新元素,并根據(jù)該函數(shù)的結(jié)果發(fā)出元素(前提是每個Observable
都至少發(fā)出過一次元素)拙友。
combineLatest
let firstSubject = PublishSubject<String>()
let secondSubject = PublishSubject<String>()
firstSubject
.withLatestFrom(secondSubject) { $0 + $1 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
/// 不會發(fā)送为狸,因為secondSubject沒有最新元素
firstSubject.onNext("???")
/// 不會發(fā)送,因為secondSubject沒有最新元素
firstSubject.onNext("???")
/// 不會發(fā)送遗契,只有當firstSubject發(fā)送元素時才會觸發(fā)
secondSubject.onNext("1")
/// 不會發(fā)送辐棒,只有當firstSubject發(fā)送元素時才會觸發(fā)
secondSubject.onNext("2")
/// 會發(fā)送
firstSubject.onNext("??")
/// 輸出結(jié)果:
??2
2.withLatestFrom
:當?shù)谝粋€Observable
發(fā)出一個元素時,立即取出第二個Observable
中的最新元素牍蜂,通過一個組合函數(shù)將兩個最新的元素合并后發(fā)送出去(若第二個Observable
沒有最新的元素則不發(fā)送)漾根。
withLatestFrom
3.startWith
:在Observable
發(fā)送元素之前,發(fā)送指定的元素鲫竞。
Observable.of(1, 2, 3)
.startWith(0)
.startWith(-1)
.startWith(-2)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
/// 輸出結(jié)果:后插入的先發(fā)送
-2
-1
0
1
2
3
五.轉(zhuǎn)換Observables
1.map
:通過一個轉(zhuǎn)換函數(shù)辐怕,將Observable
的每個元素都轉(zhuǎn)換一遍。
Observable.of(1, 2, 3)
/// map不會創(chuàng)建新的Observable从绘,只是將轉(zhuǎn)換后的元素賦值給原Observable
.map { $0 * 10 }
/// 此處訂閱的是Observable.of(1, 2, 3)創(chuàng)建的可觀察序列
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
2.flatMap
:對原有Observable
中的元素進行轉(zhuǎn)換寄疏,每一個元素返回一個新的Observable
是牢,然后將這些Observable
合并成一個新的Observable
。
struct Player {
var score: BehaviorRelay<Int>
}
let first = BehaviorSubject(value: "????")
let second = BehaviorSubject(value: "???")
let subject = BehaviorSubject(value: first)
subject.asObservable()
/// flatMap可直接返回一個Observable陕截,或者創(chuàng)建一個新的Observable返回
.flatMap { $0 }
/// 此處訂閱的不是subject驳棱,而是flatMap轉(zhuǎn)換后的first
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
/// second發(fā)出Event,因為second沒有被訂閱艘策,所以不能接收到并打印
second.onNext("??")
/// first發(fā)出事件蹈胡,因為fitst被訂閱了,所以能接收到并打印
first.onNext("??")
/// subject發(fā)出事件朋蔫,對象是second罚渐,因此second也被訂閱了
/// 但是first的訂閱并沒有被取消
subject.onNext(second)
/// second發(fā)出事件,因為second被訂閱了驯妄,所以能接收到并打印
second.onNext("???")
/// first再次發(fā)出事件荷并,依然能被接收到并打印
first.onNext("??")
/// 輸出結(jié)果:第三個元素是??,是因為BehaviorSubject在訂閱時會發(fā)出最新的元素
????
??
??
???
??
3.flatMapLatest
:當源序列有新的事件發(fā)生的時候青扔,會自動取消上一個事件的訂閱源织,轉(zhuǎn)到新的事件的訂閱上面。
六.操作符
1.share(replay:)
shareRepley
解決有多個訂閱者的情況下微猖,map映射會被執(zhí)行多次的問題:
func test() {
let subject = PublishSubject<Int>()
let user = subject.map { age -> UserModel in
print("映射---\(age)")
return UserModel(age: age)
}.share(replay: 1)
user.subscribe(onNext: { model in
print("第一次訂閱---", Unmanaged.passUnretained(model).toOpaque())
}).disposed(by: disposeBag)
subject.onNext(1)
subject.onNext(2)
/// 訂閱的同時會立即收到最新元素結(jié)果
/// 此處會收到subject.onNext(2)的結(jié)果
user.subscribe(onNext: { model in
print("第二次訂閱---", Unmanaged.passUnretained(model).toOpaque())
}).disposed(by: disposeBag)
subject.onNext(3)
subject.onNext(4)
/// 訂閱的同時會立即收到最新元素結(jié)果
/// 此處會收到subject.onNext(4)的結(jié)果
user.subscribe(onNext: { model in
print("第三次訂閱---", Unmanaged.passUnretained(model).toOpaque())
}).disposed(by: disposeBag)
subject.onCompleted()
}
映射---1
第一次訂閱--- 0x000000028151b920
映射---2
第一次訂閱--- 0x0000000281571140
第二次訂閱--- 0x0000000281571140
映射---3
第一次訂閱--- 0x0000000281570da0
第二次訂閱--- 0x0000000281570da0
映射---4
第一次訂閱--- 0x00000002815709e0
第二次訂閱--- 0x00000002815709e0
第三次訂閱--- 0x00000002815709e0
結(jié)論:一個信號不管有多少個訂閱者谈息,映射代碼只執(zhí)行一次,所有訂閱者對同一個信號處理是共享同一個結(jié)果的(輸出對象的地址是相同的)凛剥。
2.distinctUntilChanged
:阻止Observable
發(fā)出相同的元素侠仇,如果后一個元素和前一個元素是相同的,那么這個元素將不會被發(fā)出來犁珠。
Observable.of("??", "??", "??", "??", "??", "??", "??")
.distinctUntilChanged()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
/// 輸出結(jié)果:
??
??
??
??
??