RxSwift

一.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夹攒、onErroronCompleted。這樣會導致無序調(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é)果:
??
??
??
??
??
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逻炊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子犁享,更是在濱河造成了極大的恐慌余素,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炊昆,死亡現(xiàn)場離奇詭異桨吊,居然都是意外死亡,警方通過查閱死者的電腦和手機凤巨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門屏积,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人磅甩,你說我怎么就攤上這事±崖” “怎么了卷要?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵渣聚,是天一觀的道長。 經(jīng)常有香客問我僧叉,道長奕枝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任瓶堕,我火速辦了婚禮隘道,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郎笆。我一直安慰自己谭梗,他們只是感情好,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布宛蚓。 她就那樣靜靜地躺著激捏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凄吏。 梳的紋絲不亂的頭發(fā)上远舅,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音痕钢,去河邊找鬼图柏。 笑死,一個胖子當著我的面吹牛任连,可吹牛的內(nèi)容都是我干的蚤吹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼课梳,長吁一口氣:“原來是場噩夢啊……” “哼距辆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起暮刃,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跨算,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椭懊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诸蚕,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年氧猬,在試婚紗的時候發(fā)現(xiàn)自己被綠了背犯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盅抚,死狀恐怖漠魏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妄均,我是刑警寧澤柱锹,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布哪自,位于F島的核電站,受9級特大地震影響禁熏,放射性物質(zhì)發(fā)生泄漏壤巷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一瞧毙、第九天 我趴在偏房一處隱蔽的房頂上張望胧华。 院中可真熱鬧,春花似錦宙彪、人聲如沸矩动。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铅忿。三九已至,卻和暖如春灵汪,著一層夾襖步出監(jiān)牢的瞬間檀训,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工享言, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峻凫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓览露,卻偏偏與公主長得像荧琼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子差牛,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容