一、概述
二吴裤、Single
三旧找、Completable
四、Maybe
五麦牺、Driver
六钮蛛、Signal
七鞭缭、ControlEvent
一、概述
任何序列都可以用Observable
描述魏颓,創(chuàng)建序列 -> 訂閱序列 -> 信號(hào)發(fā)送 -> 信號(hào)接收岭辣。
Observable<Any>.create { (observer) -> Disposable in
observer.onNext("信號(hào)1")
return Disposables.create()
}.subscribe(onNext: { (val) in
print("信號(hào)接收區(qū):\(val)")
}).disposed(by: disposeBag)
Observable
是通用序列的描述符,調(diào)用.onNext
甸饱,.onError
沦童,onCompleted
來(lái)發(fā)送信號(hào),通用性強(qiáng)柜候,但針對(duì)特殊需求可能會(huì)覺(jué)得繁瑣搞动,因此RxSwift
還提供了一組特征序列,是Observable
序列的變種渣刷,它能夠幫助我們更準(zhǔn)確的描述序列鹦肿。即Single
、Completable
辅柴、Maybe
箩溃、Driver
、Signal
碌嘀、ControlEvent
涣旨。
二、Single
1股冗、定義
單元素序列霹陡,信號(hào)只發(fā)送一次,響應(yīng)信號(hào)或錯(cuò)誤信號(hào)止状。
Single<Any>.create { (single) -> Disposable in
single(.success("假裝我是一個(gè)正兒八經(jīng)的數(shù)據(jù)"))
//single(.error(NSError.init(domain: "網(wǎng)絡(luò)出現(xiàn)錯(cuò)誤", code: 101, userInfo:["name":"hibo"])))
return Disposables.create()
}.subscribe(onSuccess: { (val) in
print("Single:\(val)")
}) { (error) in
print("SingleError:\(error)")
}.disposed(by: disposeBag)
-
sinngle(.success(data))
->onSuccess
發(fā)送響應(yīng)元素到成功觀察者 -
sinngle(.error(error))
->error
發(fā)送錯(cuò)誤元素到錯(cuò)誤觀察者
響應(yīng)元素和錯(cuò)誤元素分開(kāi)處理烹棉,此時(shí)我們可以聯(lián)想到應(yīng)用中的網(wǎng)絡(luò)請(qǐng)求,成功數(shù)據(jù)用來(lái)渲染怯疤,錯(cuò)誤數(shù)則據(jù)彈出提示框浆洗。
2、源碼探索
2.1集峦、Single定義
/// Sequence containing exactly 1 element
public enum SingleTrait { }
/// Represents a push style sequence containing 1 element.
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
public enum SingleEvent<Element> {
/// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
case success(Element)
/// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
case error(Swift.Error)
}
定位到Single.swift
文件伏社,首先能看到Single
是PrimitiveSequence
結(jié)構(gòu)體類型的別名,SingleEvent
是事件枚舉塔淤,有success
和error
兩個(gè)成員變量摘昌。
2.2、create
創(chuàng)建序列高蜂。代碼如下(此處代碼標(biāo)記為1??):
extension PrimitiveSequenceType where TraitType == SingleTrait {
public typealias SingleObserver = (SingleEvent<ElementType>) -> Void
//代碼省略若干行
public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
let source = Observable<ElementType>.create { observer in
return subscribe { event in
switch event {
case .success(let element):
observer.on(.next(element))
observer.on(.completed)
case .error(let error):
observer.on(.error(error))
}
}
}
return PrimitiveSequence(raw: source)
}
}
首先看參數(shù)是一個(gè)帶Disposable
類型返回值的閉包第焰,交由外部(業(yè)務(wù)層)實(shí)現(xiàn),內(nèi)部調(diào)用向外傳入一個(gè)SingleObserver
閉包妨马,以上寫(xiě)法不太好理解挺举,我們可以換一種寫(xiě)法:
public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
let source = Observable<ElementType>.create { observer in
// 1、內(nèi)部實(shí)現(xiàn)一個(gè)閉包烘跺,用來(lái)接收外界傳入的SingleEvent信號(hào)湘纵,接著做進(jìn)一步的信號(hào)發(fā)送
let block = { (event:SingleEvent<ElementType>) -> Void in
switch event {
case .success(let element):
observer.on(.next(element))
observer.on(.completed)
case .error(let error):
observer.on(.error(error))
}
}
// 2、調(diào)用外部實(shí)現(xiàn)的閉包方法滤淳,向外部發(fā)送內(nèi)部實(shí)現(xiàn)的閉包方法做連接作用
let disposable = subscribe(block)//3梧喷、返回值Disposable對(duì)象
return disposable
}
return PrimitiveSequence(raw: source)//4、創(chuàng)建PrimitiveSequence對(duì)象并保存Observable序列對(duì)象
}
- 內(nèi)部實(shí)現(xiàn)一個(gè)閉包
block
脖咐,用來(lái)接收外界傳入的SingleEvent
信號(hào)铺敌,接著做進(jìn)一步的信號(hào)發(fā)送 - 調(diào)用外部實(shí)現(xiàn)的閉包方法,將內(nèi)部實(shí)現(xiàn)的閉包
block
發(fā)送出去屁擅,起連接作用 - 創(chuàng)建
PrimitiveSequence
對(duì)象并保存Observable
序列對(duì)象source
偿凭,返回PrimitiveSequence
對(duì)象
在create
方法內(nèi)部實(shí)際上實(shí)現(xiàn)了一個(gè)Observable
序列,由此可見(jiàn)Single
序列是對(duì)Observable
序列的封裝派歌,Disposable
對(duì)象通過(guò)閉包交由業(yè)務(wù)層創(chuàng)建弯囊,Single
序列在實(shí)現(xiàn)上,方式方法與Observable
保持一致胶果,此處可稱一絕匾嘱。當(dāng)前我們只探索Single
的信號(hào)是如何完成傳遞,Observable
序列的信號(hào)傳遞流程在《Swift核心源碼探索》中有詳細(xì)介紹早抠。
2.3霎烙、訂閱序列
也是在同PrimitiveSequenceType
擴(kuò)展中定義,代碼如下(此處代碼標(biāo)記為2??):
public func subscribe(onSuccess: ((ElementType) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil) -> Disposable {
#if DEBUG
let callStack = Hooks.recordCallStackOnError ? Thread.callStackSymbols : []
#else
let callStack = [String]()
#endif
return self.primitiveSequence.subscribe { event in
switch event {
case .success(let element):
onSuccess?(element)
case .error(let error):
if let onError = onError {
onError(error)
} else {
Hooks.defaultErrorHandler(callStack, error)
}
}
}
}
方法中先調(diào)用了self.primitiveSequence
方法蕊连,返回了self
悬垃,方法是在遵循PrimitiveSequenceType
協(xié)議的PrimitiveSequence
的擴(kuò)展中,為了保證協(xié)議的一致性咪奖。代碼如下:
extension PrimitiveSequence: PrimitiveSequenceType {
/// Additional constraints
public typealias TraitType = Trait
/// Sequence element type
public typealias ElementType = Element
// Converts `self` to primitive sequence.
///
/// - returns: Observable sequence that represents `self`.
public var primitiveSequence: PrimitiveSequence<TraitType, ElementType> {
return self
}
}
緊接著調(diào)用另一個(gè)subscribe
方法盗忱,代碼如下(此處代碼標(biāo)記為3??):
public func subscribe(_ observer: @escaping (SingleEvent<ElementType>) -> Void) -> Disposable {
var stopped = false
return self.primitiveSequence.asObservable().subscribe { event in
if stopped { return }
stopped = true
switch event {
case .next(let element):
observer(.success(element))
case .error(let error):
observer(.error(error))
case .completed:
rxFatalErrorInDebug("Singles can't emit a completion event")
}
}
}
self.primitiveSequence -> asObservable() -> subscribe
- 此處截?cái)嗔?code>completed信號(hào)的向上傳遞,因此
Single
序列只能收到響應(yīng)信號(hào)和錯(cuò)誤信號(hào)
該段代碼也調(diào)用了self.primitiveSequence
方法羊赵,接著調(diào)用asObservable()
方法趟佃,查看代碼發(fā)現(xiàn)此處是為了獲取source
對(duì)象,即Observable
可觀察序列昧捷。
再查看subscribe
的方法(此處標(biāo)記為代碼4??):
public func subscribe(_ on: @escaping (Event<E>) -> Void)
-> Disposable {
let observer = AnonymousObserver { e in
on(e)
}
return self.asObservable().subscribe(observer)
}
代碼創(chuàng)建了一個(gè)觀察者闲昭,當(dāng)前觀察者將會(huì)收到發(fā)送過(guò)來(lái)的消息,并由此通過(guò)閉包一層層傳到業(yè)務(wù)層靡挥。 4?? -> 3?? -> 2?? -> 1?? ->業(yè)務(wù)層
當(dāng)前self指向的是1??處創(chuàng)建并保存的
Observable
類型的source
對(duì)象序矩,因此該處subscribe
所調(diào)用的即是Produce
類中的subscribe
方法,在方法內(nèi)部創(chuàng)建了sink
對(duì)象跋破,來(lái)觸發(fā)創(chuàng)建序列時(shí)實(shí)現(xiàn)的閉包簸淀,即代碼1??處所create
后的閉包此時(shí)就到了業(yè)務(wù)層瓶蝴,通過(guò)create內(nèi)部實(shí)現(xiàn)的閉包
single
向內(nèi)部發(fā)送消息,再有observer
調(diào)用on
來(lái)向觀察者發(fā)送信號(hào)信號(hào)發(fā)送不做贅述租幕,最終會(huì)到達(dá)4??處代碼的觀察者舷手,此時(shí)再由閉包一層層向上傳遞,直到業(yè)務(wù)層的監(jiān)聽(tīng)閉包
總結(jié):
序列的產(chǎn)生劲绪,訂閱男窟,發(fā)送,接收還是由Observable
來(lái)實(shí)現(xiàn)的贾富,Single
只是對(duì)Observable
做了封裝歉眷,去除了onCompleted
的消息監(jiān)聽(tīng)及消息發(fā)送。
具體的Observable序列產(chǎn)生到觀察流程見(jiàn)《Swift核心源碼探索》
三颤枪、Completable
只能產(chǎn)生completed
事件和error
事件汗捡,沒(méi)有序列元素值產(chǎn)生。
Completable.create { (completable) -> Disposable in
completable(.completed)
//completable(.error(NSError.init(domain: "出現(xiàn)異常", code: 101, userInfo: nil)))
return Disposables.create()
}.subscribe(onCompleted: {
print("Completable")
}) { (error) in
print(error)
}.disposed(by: disposeBag)
- 應(yīng)用場(chǎng)景汇鞭,只關(guān)心任務(wù)是否完成凉唐,不關(guān)心不需要結(jié)果
- 在
Competable.swift
下,在PrimitiveSequenceType
擴(kuò)展中實(shí)現(xiàn)了序列的創(chuàng)建霍骄,訂閱台囱,即信號(hào)轉(zhuǎn)發(fā)
定義如下:
/// Sequence containing 0 elements
public enum CompletableTrait { }
/// Represents a push style sequence containing 0 elements.
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
public enum CompletableEvent {
/// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
同樣Completable
類也是PrimitiveSequence
的別名,并聲明一個(gè)枚舉包含读整,error
和completed
成員變量簿训,限定了事件產(chǎn)生類型。都是對(duì)Observable
序列的封裝米间,源碼此處不做探索說(shuō)明强品,和Single
一致,只是在訂閱階段對(duì).next
事件做了攔截屈糊。
四的榛、Maybe
和Single
序列相似,發(fā)出一個(gè)元素或一個(gè)completed
事件或error
事件逻锐。
Maybe<Any>.create { (maybe) -> Disposable in
maybe(.success("element"))
//maybe(.completed)
//maybe(.error(NSError.init(domain: "出現(xiàn)異常", code: 101, userInfo: nil)))
return Disposables.create()
}.subscribe(onSuccess: { (val) in
print(val)
}, onError: { (error) in
print("error:\(error)")
}) {
print("completed")
}.disposed(by: disposeBag)
在開(kāi)發(fā)中夫晌,如果一個(gè)業(yè)務(wù)有時(shí)候需要一個(gè)元素,有時(shí)候只需要知道處理完成的時(shí)候昧诱,可以使用該Maybe
晓淀,解決不確定需求問(wèn)題。源碼探索略盏档,同上凶掰。
五、Driver
-
Driver
序列不會(huì)產(chǎn)生error
事件 - 在主線程中監(jiān)聽(tīng),會(huì)向新訂閱者發(fā)送上次發(fā)送過(guò)的元素懦窘,簡(jiǎn)化
UI
層的代碼 - 共享序列
下面看一下為什么會(huì)擴(kuò)展一個(gè)Driver
序列前翎。
有一個(gè)需求:
- 搜索框中每次輸入一個(gè)文本,獲取一次網(wǎng)絡(luò)請(qǐng)求奶赠,成功后渲染
UI
鱼填,多個(gè)控件顯示
先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的UI
:
let tf = UITextField.init(frame: CGRect(x: 100, y: 100, width: 200, height: 40))
tf.borderStyle = .roundedRect
tf.placeholder = "請(qǐng)輸入"
self.view.addSubview(tf)
let label1 = UILabel.init(frame: CGRect(x: 100, y: 160, width: 200, height: 40))
label1.backgroundColor = .groupTableViewBackground
label1.textAlignment = .center
self.view.addSubview(label1)
let label2 = UILabel.init(frame: CGRect(x: 100, y: 210, width: 200, height: 40))
label2.backgroundColor = .groupTableViewBackground
label2.textAlignment = .center
self.view.addSubview(label2)
創(chuàng)建了一個(gè)textfield
,兩個(gè)label
用來(lái)展示毅戈。下面在來(lái)實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)請(qǐng)求,返回一個(gè)Single
序列:
func network(text:String) -> Single<Any> {
return Single<Any>.create(subscribe: { (single) -> Disposable in
if text == "1234"{
single(.error(NSError.init(domain: "出現(xiàn)錯(cuò)誤", code: 101, userInfo: nil)))
}
DispatchQueue.global().async {
print("請(qǐng)求網(wǎng)絡(luò)")
single(.success(text))
}
return Disposables.create()
})
}
網(wǎng)絡(luò)請(qǐng)求為耗時(shí)操作愤惰,因此我們?cè)诋惒街衼?lái)完成苇经,直接發(fā)送序列,假裝我們請(qǐng)求了一次網(wǎng)絡(luò)宦言。
1扇单、普通方法實(shí)現(xiàn)textfield
輸入序列的監(jiān)聽(tīng),并調(diào)取網(wǎng)絡(luò)請(qǐng)求方法:
let result = tf.rx.text.orEmpty.skip(1)
.flatMap{
return self.network(text: $0)
.observeOn(MainScheduler.instance)
.catchErrorJustReturn("網(wǎng)絡(luò)請(qǐng)求失敗")
}.share(replay: 1, scope: .whileConnected)
//網(wǎng)絡(luò)請(qǐng)求將發(fā)送多次請(qǐng)求
result.subscribe(onNext: { (val) in
print("訂閱一:\(val) 線程:\(Thread.current)")
}).disposed(by: disposeBag)
result.subscribe(onNext: { (val) in
print("訂閱二:\(val) 線程:\(Thread.current)")
}).disposed(by: disposeBag)
result.map{"\(($0 as! String).count)"}.bind(to: label1.rx.text).disposed(by: disposeBag)
result.map{"\($0)"}.bind(to: label2.rx.text).disposed(by: disposeBag)
-
flatMap
將原序列轉(zhuǎn)換為Observables奠旺,將這些Observables的元素合并之后發(fā)出 -
observeOn
選擇在哪個(gè)線程執(zhí)行 -
catchErrorJustReturn
錯(cuò)誤處理蜘澜,將onError
事件轉(zhuǎn)為onNext
事件 -
share
為多個(gè)觀察者共享資源,網(wǎng)絡(luò)請(qǐng)求只發(fā)送呢一次响疚,否則多個(gè)訂閱將會(huì)觸發(fā)多個(gè)請(qǐng)求
2鄙信、Driver實(shí)現(xiàn):
let result = tf.rx.text.orEmpty
.asDriver()
.flatMap {
return self.network(text: $0).asDriver(onErrorJustReturn: "網(wǎng)絡(luò)請(qǐng)求失敗")
}
result.map{"長(zhǎng)度:\(($0 as! String).count)"}
.drive(label1.rx.text).disposed(by: disposeBag)
result.map{"\($0)"}.drive(label2.rx.text).disposed(by: disposeBag)
-
asDriver()
將序列轉(zhuǎn)換為driver
序列 -
map
重新組合并生成新的序列 -
driver
將元素在主線程中綁定到label1
和label2
上
相比非driver
下的代碼實(shí)現(xiàn),driver
序列省去了線程的設(shè)置忿晕,share
數(shù)據(jù)共享設(shè)置装诡。
Driver源碼探索
斷點(diǎn)查看asDriver()
方法:
extension ControlProperty {
/// Converts `ControlProperty` to `Driver` trait.
///
/// `ControlProperty` already can't fail, so no special case needs to be handled.
public func asDriver() -> Driver<E> {
return self.asDriver { _ -> Driver<E> in
#if DEBUG
rxFatalError("Somehow driver received error from a source that shouldn't fail.")
#else
return Driver.empty()
#endif
}
}
}
是ControlProperty
的擴(kuò)展方法,返回了一個(gè)Driver<E>
類践盼,Driver
是SharedSequence
的別名鸦采,用來(lái)描述不同類型的序列,最后又調(diào)用了asDriver
方法咕幻,而該方法在ObservableConvertibleType
的擴(kuò)展中渔伯,一直追蹤會(huì)發(fā)現(xiàn)很多類都是繼承自ObservableConvertibleType
下。
extension ObservableConvertibleType {
public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<E>) -> Driver<E> {
let source = self
.asObservable()
.observeOn(DriverSharingStrategy.scheduler)
.catchError { error in
onErrorRecover(error).asObservable()
}
return Driver(source)
}
}
如上代碼也設(shè)置了observerOn
方法肄程,來(lái)指定線程锣吼,繼續(xù)深入能夠發(fā)現(xiàn)DriverSharingStrategy.scheduler
內(nèi)部指定的就是主線程,印證了上面所說(shuō)的Driver
的執(zhí)行是在主線程的绷耍。最后初始化一個(gè)Driver
對(duì)象返回吐限,看一下初始化過(guò)程,及對(duì)SharedSequence
類的初始化褂始,代碼如下:
public struct SharedSequence<S: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType {
public typealias E = Element
public typealias SharingStrategy = S
let _source: Observable<E>
init(_ source: Observable<E>) {
self._source = S.share(source)
}
}
此處調(diào)用了share
并傳入了可觀察序列诸典,感覺(jué)好像在哪見(jiàn)過(guò),此處猜想它是用來(lái)共享序列的,使用lldb:po S.self
查找share
所在位置:RxCocoa.DriverSharingStrategy
狐粱,cmd+點(diǎn)擊
進(jìn)入舀寓,代碼如下:
public typealias Driver<E> = SharedSequence<DriverSharingStrategy, E>
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { return SharingScheduler.make() }
public static func share<E>(_ source: Observable<E>) -> Observable<E> {
return source.share(replay: 1, scope: .whileConnected)
}
}
在此處傳入的序列用來(lái)掉用share
,使得當(dāng)前序列作為共享序列肌蜻,即driver
序列為共享序列互墓。
六、Signal
與Driver
相似蒋搜,不會(huì)產(chǎn)生error
事件篡撵,在主線程執(zhí)行,但不會(huì)像Driver
一樣會(huì)給新觀察者發(fā)送上一次發(fā)送的元素豆挽。
使用如下:
let event : Driver<Void> = button.rx.tap.asDriver()
event.drive(onNext: {
print("yahibo")
event.drive(onNext: {
print("yahibo1")
}).disposed(by: self.disposeBag)
}).disposed(by: disposeBag)
運(yùn)行打印育谬,發(fā)現(xiàn)在點(diǎn)擊后重新訂閱的觀察者,會(huì)直接收到點(diǎn)擊事件帮哈,這是我們業(yè)務(wù)不允許的膛檀。下面再看Signal
序列:
let event : Signal<Void> = button.rx.tap.asSignal()
event.emit(onNext: {
print("yahibo")
event.emit(onNext: {
print("yahibo1")
}).disposed(by: self.disposeBag)
}).disposed(by: disposeBag)
運(yùn)行結(jié)果,每一個(gè)新序列都會(huì)在點(diǎn)擊后觸發(fā)娘侍。
七咖刃、ControlEvent
專門(mén)用于描述UI
控件所產(chǎn)生的事件,不會(huì)產(chǎn)生error
事件憾筏,在主線程中監(jiān)聽(tīng)嚎杨。代碼如下:
1、監(jiān)聽(tīng)點(diǎn)擊事件
let event : ControlEvent<Void> = button.rx.tap.asControlEvent()
event.bind(onNext: {
print("controllerEvent")
}).disposed(by: disposeBag)
2踩叭、監(jiān)聽(tīng)點(diǎn)擊事件并綁定數(shù)據(jù)到其他UI
let event : ControlEvent<Void> = button.rx.tap.asControlEvent()
event.map{"yahibo"}.bind(to: label1.rx.text).disposed(by: disposeBag)
總結(jié):
以上序列都是基于Observable
的磕潮,是對(duì)其更高層的封裝,針對(duì)不同應(yīng)用場(chǎng)景設(shè)計(jì)容贝,簡(jiǎn)化不同場(chǎng)景下序列的使用流程自脯。