RxSwift學(xué)習(xí)--核心邏輯初探

前言

寫這篇文章是為了記錄下自己在對于RxSwift的學(xué)習(xí)過程中的概念理解绑榴,操作步驟以及心得體會,以便于在以后復(fù)習(xí)所用。如果文中有任何錯誤的地方稚虎,還請各位看官老爺們指正...(先捂上臉??)

函數(shù)響應(yīng)式編程

在學(xué)習(xí)RxSwift之前,先來了解一下函數(shù)響應(yīng)式編程的思想偎捎,我們可以把函數(shù)響應(yīng)式編程拆開來看蠢终,分為函數(shù)式編程和響應(yīng)式編程來理解:

1.函數(shù)式編程

函數(shù)式編程簡稱FP(Functional Programming),函數(shù)式編程就是一種抽象程度很高的編程范式茴她,它將計算機運算看做是數(shù)學(xué)中函數(shù)的計算寻拂,而純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量,因此丈牢,任意一個函數(shù)兜喻,只要輸入是確定的,輸出就是確定的赡麦,這種純函數(shù)我們稱之為沒有副作用朴皆。而允許使用變量的程序設(shè)計語言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定泛粹,同樣的輸入遂铡,可能得到不同的輸出,因此晶姊,這種函數(shù)是有副作用的扒接。

函數(shù)式編程的一個特點就是:允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù),同時還允許返回一個函數(shù)们衙!

函數(shù)表達式: y = f(x) ---> x = f(x) ---> y = f(f(x))

下面寫一個栗子??來理解一下:
有這樣一個需求:對于數(shù)組[1,2,3,4,5,6,7]钾怔,首先獲取 > 3的數(shù)字,獲取到的數(shù)字之后 + 1蒙挑,再輸出所有數(shù)字中的偶數(shù)

let array = [1,2,3,4,5,6,7]
for num in array{
            if num > 3{
                let number = num + 1
                if (number % 2 == 0) {
                    print(number)
                  }
            }
        }

這里我們利用行為式思路解決了這個需求宗侦,下面來換個思路來解決,使用 Arrayfilter方法;

let array = [1,2,3,4,5,6,7]
 array.filter{ $0 > 3}
            .filter{ ($0+1) % 2 == 0 }
            .forEach { print($0) }

這里 array.filter 函數(shù)接受一個閉包類型的參數(shù)忆蚀,filter方法會對 array 中的每一個元素都用傳入filter 的閉包調(diào)用一遍矾利,根據(jù)這個閉包的返回值決定是否將這個元素作為符合條件的元素加入我們的查找結(jié)果中。

簡單來說我們只需要在傳入的閉包中聲明好查找的規(guī)則馋袜,這樣我們就完成整個查找操作的處理了男旗。我們這里并沒有告訴程序應(yīng)該怎么去查找滿足條件的元素的方法,而只是聲明了一個規(guī)則欣鳖。這樣做最大的好處就是能夠減少我們的代碼量察皇,讓我們的代碼看起來非常的簡潔,而且易理解泽台。 這種方式就是函數(shù)式編程的一個例子什荣。

2.響應(yīng)式編程

響應(yīng)式編程簡稱RP(Reactive Programming),響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的異步編程范式呀忧。這意味著可以在編程語言中很方便地表達靜態(tài)或動態(tài)的數(shù)據(jù)流,而相關(guān)的計算模型會自動將變化的值通過數(shù)據(jù)流進行傳播溃睹。

簡單的來說就是基于事件流的編程方式而账。事件流就是將要發(fā)生事件按照時間順序排序發(fā)生形成的。而當這個事件流產(chǎn)生了返回的數(shù)據(jù)因篇,可以立刻得到通知并調(diào)用回調(diào)函數(shù)去處理數(shù)據(jù)泞辐。(在現(xiàn)實生活中就是:我在家拿起手機預(yù)訂了一份外賣,然后商家會收到訂單竞滓,開始制作外賣咐吼,制作完成后會通知騎手取餐,騎手接單取餐商佑,然后送餐锯茄,到達目的地,外賣送達茶没,訂單完成肌幽,這樣一系列的事件,先稱它為事件流抓半,如果在這些事件流進行的過程中喂急,比如說商家沒有出餐,或者騎手沒有取餐等等笛求,這些都會導(dǎo)致這個事件流無法完成廊移,而我們可以根據(jù)這個事件流中的任何一個事件的結(jié)果來進行響應(yīng))

從這個小例子可以看到組成響應(yīng)式編程的三種動作(數(shù)據(jù)):事件(數(shù)據(jù)),錯誤探入,結(jié)束狡孔。通過得到這三個響應(yīng)式動作,我們就可以在程序中作出不同的響應(yīng)蜂嗽。

這里個人覺得有點很重要苗膝,就是要對事件進行“預(yù)訂/監(jiān)聽”,然后才能收到這個事件的反饋徒爹。而對于我而言荚醒,我就是監(jiān)聽事件的人芋类,也就是“觀察者/訂閱方”隆嗅,監(jiān)聽+觀察者是不是就是我們比較熟悉的觀察者模式,那么響應(yīng)式編程就是觀察者模式+事件流的控制侯繁。

3.函數(shù)響應(yīng)式編程

函數(shù)響應(yīng)式編程 FRP(Functional Reactive Programming)是函數(shù)式編程與響應(yīng)式編程相結(jié)合起來的胖喳,響應(yīng)式編程思想為體, 函數(shù)式編程思想為用。(比較經(jīng)典的框架就是RAC和RxSwift)贮竟,常常有人說丽焊,F(xiàn)RP能讓你的代碼像數(shù)學(xué)一樣簡潔较剃,業(yè)務(wù)像流水一樣清晰流暢。

函數(shù)響應(yīng)式

RxSwift初識

1.RxSwift簡介

首先技健,ReactiveX(簡寫: Rx) 是一個可以幫助我們簡化異步編程的框架,簡單來說就是基于異步 Event序列的響應(yīng)式編程,并提供更優(yōu)雅的數(shù)據(jù)綁定,可以時刻響應(yīng)新的數(shù)據(jù)同時順序地處理它們写穴。RxSwift (ReactiveX for Swift),就是ReactiveXSwift版本
ReactiveX家族非常強大,就如同‘毒液家族’一樣的強大雌贱,除了我后面會學(xué)習(xí)的 RxSwift 之外啊送,還有 RAC(ReactiveCocoa), RxJava, RxJS, RxKotlin, Rx.NET...等等.

2.RxSwift導(dǎo)入配置

(1) 手動導(dǎo)入

  • RxSwift GitHub 上下載最新的代碼,
  • 將下載下來的源碼包中 Rx.xcodeproj 拖拽至工程中,
  • Project -> Targets -> General -> Embedded Binaries 配置項, RxSwift.framework欣孤、RxCocoa.framework 添加進來即可
  • 在需要使用 RxSwift的地方import 進來

(2) CocoaPods導(dǎo)入

# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
    pod 'RxSwift', '~> 5.0'
    pod 'RxCocoa', '~> 5.0'
end

替換 YOUR_TARGET_NAME 然后在 Podfile 目錄下, 終端輸入:

$ pod install

??????這里說明一下為什么會導(dǎo)入RxSwiftRxCocoa兩個庫馋没,它們的作用分別是:

  • RxSwift:它只是基于 Swift 語言的 Rx 標準實現(xiàn)接口庫,所以 RxSwift 里不包含任何Cocoa或者 UI方面的類降传。
  • RxCocoa:是基于 RxSwift 針對于 iOS 開發(fā)的一個庫篷朵,它通過 Extension 的方法給原生的比如 UI 控件添加了 Rx的特性,使得我們更容易訂閱和響應(yīng)這些控件的事件婆排。

3.RxSwift特性

  • 復(fù)合 -Rx 就是和復(fù)合的代名詞
  • 復(fù)用 - 復(fù)用性比較強 - 代碼量降低
  • 清晰 - 因為聲明都是不可變更声旺,代碼函數(shù)式編程可讀性強
  • 易用 - 理解容易,還抽象的了異步編程段只,統(tǒng)一代碼風(fēng)格
  • 穩(wěn)定 - 因為 Rx 是完全通過單元測試的
  • 裝X - 代碼的風(fēng)格很明顯比原?好

究竟是不是這樣的呢艾少,下面通過一些常用的方式來驗證一下:

(1)KVO

一般寫法:
func setupKVO() {
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("響應(yīng)")
    print(change as Any)
}

deinit {
    self.removeObserver(self.person, forKeyPath: "name", context: nil)
}
RxSwift寫法:
func setupKVO() {
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe(onNext: { (value) in
            print(value as Any)
        })
        .disposed(by: disposeBag)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}

是不是感覺RxSwift版本的KVO寫的代碼更少了,而且也不用去實現(xiàn)觀察者的代理方法翼悴,不用關(guān)心是否遺漏removeObserver方法缚够。

(2)Target Action

一般寫法:
func setupButton() {
   button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}

@objc func didClickButton(){
    print("點我干什么")
}
RxSwift寫法:
func setupButton() {
    self.button.rx.tap
      .subscribe(onNext: { () in
            print("點擊事件")
        })
      .disposed(by: disposeBag)
}

不需要實現(xiàn)Target Action,代碼更加簡單了

(3)代理

一般寫法:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset: \(scrollView.contentOffset)")
    }
}
RxSwift寫法:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
    }
}

不需要實現(xiàn)代理方法啦,可以直接獲取到scrollview的偏移

(4)通知

一般寫法:
var testObserver: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    testObserver = NotificationCenter.default.addObserver(
          forName: .UIApplicationWillEnterForeground,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(testObserver)
}
RxSwift寫法:
override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}

不需要去關(guān)注是否已經(jīng)移除了通知鹦赎,不會因為沒有移除通知而出現(xiàn)崩潰

(5)單擊手勢

一般寫法:
func setupGestureRecognizer(){
    let tap = UITapGestureRecognizer()
    tap.addTarget(self, action: #selector(singleTap(_:)))
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    
}
    
@objc func singleTap(_ tapGesture: UITapGestureRecognizer) {
    print("點我干嘛")
}
RxSwift寫法:
func setupGestureRecognizer(){
        
    let tap = UITapGestureRecognizer()
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    tap.rx.event.subscribe(onNext: { (tap) in
        print(tap.view)
    })
    .disposed(by: disposeBag)
  }

少寫了手勢觸發(fā)實現(xiàn)的方法谍椅,代碼更簡介了

(6)Timer定時器

一般寫法:
var testtimer = Timer()

func setupTimer() {
    testtimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTimer), userInfo: nil, repeats: true)
    testtimer.fire();
    RunLoop.current.add(testtimer, forMode: .common)
}

@objc func UpdateTimer() {

   print("timer start")
    
}
RxSwift寫法:
var timer: Observable<Int>!

func setupTimer() {
    timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    timer.subscribe(onNext: { (num) in
        print(num)
    })
    .disposed(by: disposeBag)
}

這里timer更好地是不用考慮頁面上的Scrollviewtimer的影響

看完這些小例子??之后,覺得RxSwift比一般的Swift寫法要簡單好多古话,簡直就是“騷一般的操作”雏吭。

4.RxSwift核心邏輯初識

在前面的概念介紹中大概已經(jīng)知道了ReactiveX是基于異步 Event序列的響應(yīng)式編程, 并提供更優(yōu)雅的數(shù)據(jù)綁定,可以時刻響應(yīng)新的數(shù)據(jù)同時順序地處理它們,既然要響應(yīng)數(shù)據(jù),就還需要一個觀察者陪踩。

RxSwift 核心概念就可以理解為一個觀察者(Observer)訂閱一個可被觀察序列(Observable)杖们。觀察者對可被觀察序列發(fā)射的事件(Event)或事件序列作出響應(yīng)。

先來理解一下這三個類的概念:

  • Observable<T> 這個類就是 Rx 框架的基礎(chǔ)肩狂,我們可以稱它為可觀察序列摘完。它的作用就是可以異步地產(chǎn)生一系列的 Event(事件),即一個 Observable<T> 對象會隨著時間推移不定期地發(fā)出 event(element : T) 這樣一個東西傻谁。
  • 而且這些 Event 還可以攜帶數(shù)據(jù)孝治,它的泛型 <T> 就是用來指定這個 Event 攜帶的數(shù)據(jù)的類型。
  • 有了可觀察序列,我們還需要有一個 Observer(訂閱者)來訂閱它谈飒,這樣這個訂閱者才能收到 Observable<T> 不時發(fā)出的 Event岂座。

既然Observable是一個可被觀察的序列,可以異步地產(chǎn)生一系列的 Event杭措,那么查看RxSwift/Event.swift 源碼可以發(fā)現(xiàn)

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被定義成一個枚舉值费什,這也就是說一個Observable可觀察序列可以發(fā)出三種不同類型的Event事件:

  • nextnext 事件就是那個可以攜帶數(shù)據(jù) <T> 的事件,可以說它就是一個普通事件
image
  • errorerror 事件表示一個錯誤手素,它可以攜帶具體的錯誤內(nèi)容吕喘,一旦 Observable 發(fā)出了 error event,則這個Observable就等于終止了刑桑,以后它再也不會發(fā)出event` 事件了氯质。
image
  • completedcompleted事件表示 Observable 發(fā)出的事件正常地結(jié)束了,跟 error 一樣祠斧,一旦 Observable 發(fā)出了 completed event闻察,則這個 Observable 就等于終止了,以后它再也不會發(fā)出 event 事件了
image

序列監(jiān)聽有三個步驟:1.創(chuàng)建序列琢锋,2訂閱序列辕漂,3.發(fā)送信號。當創(chuàng)建序列吴超,并訂閱了序列后钉嘹,只要某個事件發(fā)送了序列消息,就可以在序列訂閱的閉包里面監(jiān)聽到發(fā)送的消息鲸阻。

下面創(chuàng)建一個可觀察序列來感受一下:

//第一步:創(chuàng)建序列
//在create()函數(shù)中傳入一個閉包跋涣,任務(wù)是對每一個過來的訂閱進行處理
 let ob = Observable<Any>.create { (observer) -> Disposable in
            // 第三步:發(fā)送信號(onCompleted和onError只能發(fā)送一個)
            observer.onNext("你好騷啊")
            observer.onCompleted()
//            observer.onError(NSError.init(domain: "loser", code: 10010, userInfo: nil))
            return Disposables.create()
 
//第二步:訂閱信息
//當我們訂閱了Observable的消息后,只要Observable的事件觸發(fā)鸟悴,都會通過onNext這個閉包告訴我們陈辱。
 let _ = ob.subscribe(onNext: { (text) in
            print("訂閱到:\(text)")    //這里會監(jiān)聽到訂閱的Observable事件
        }, onError: { (error) in
            print("error: \(error)")    //當發(fā)生錯誤時,會回調(diào)這里
        }, onCompleted: { // 當序列執(zhí)行完畢時细诸,會回調(diào)這里沛贪。
            print("完成")
        }) {
            print("銷毀") 
        }
        .disposed(by: disposeBag)

DisposeBag:作用是 Rx 在視圖控制器或者其持有者將要銷毀的時候,自動釋法掉綁定在它上面的資源震贵。它是通過類似“訂閱處置機制”方式實現(xiàn)(類似于 NotificationCenterremoveObserver)利赋。

這就算是RxSwift響應(yīng)式的核心邏輯了,這里有點疑惑就是:為什么觀察者發(fā)出的信號,可觀察序列能夠訂閱到呢?

未完待續(xù)......

注:感謝下面的參考文檔

RxSwift 中文文檔

航歌Swift

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猩系,一起剝皮案震驚了整個濱河市媚送,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝙眶,老刑警劉巖季希,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褪那,死亡現(xiàn)場離奇詭異幽纷,居然都是意外死亡式塌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門友浸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峰尝,“玉大人,你說我怎么就攤上這事收恢∥溲В” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵伦意,是天一觀的道長火窒。 經(jīng)常有香客問我,道長驮肉,這世上最難降的妖魔是什么熏矿? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮离钝,結(jié)果婚禮上票编,老公的妹妹穿的比我還像新娘。我一直安慰自己卵渴,他們只是感情好慧域,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浪读,像睡著了一般昔榴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碘橘,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天论泛,我揣著相機與錄音,去河邊找鬼蛹屿。 笑死屁奏,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的错负。 我是一名探鬼主播坟瓢,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼犹撒!你這毒婦竟也來了折联?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤识颊,失蹤者是張志新(化名)和其女友劉穎诚镰,沒想到半個月后奕坟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡清笨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年月杉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抠艾。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡苛萎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出检号,到底是詐尸還是另有隱情腌歉,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布齐苛,位于F島的核電站翘盖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凹蜂。R本人自食惡果不足惜馍驯,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炊甲。 院中可真熱鬧泥彤,春花似錦、人聲如沸卿啡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颈娜。三九已至剑逃,卻和暖如春下硕,著一層夾襖步出監(jiān)牢的瞬間蛉腌,已是汗流浹背纳账。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工春哨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玩裙。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓叹哭,卻偏偏與公主長得像勤揩,于是被迫代替她去往敵國和親俗批。 傳聞我的和親對象是個殘疾皇子俗或,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

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