寫這篇文章是爲了記錄下本身在對於RxSwift的學習過程當中的概念理解,操做步驟以及心得體會,以便於在之後複習所用。若是文中有任何錯誤的地方,還請各位看官老爺們指正...(先捂上臉🤦)html
在學習RxSwift以前,先來了解一下函數響應式編程的思想,咱們能夠把函數響應式編程拆開來看,分爲函數式編程和響應式編程來理解:react
函數式編程簡稱FP(Functional Programming),函數式編程就是一種抽象程度很高的編程範式,它將計算機運算看作是數學中函數的計算,而純粹的函數式編程語言編寫的函數沒有變量,所以,任意一個函數,只要輸入是肯定的,輸出就是肯定的,這種純函數咱們稱之爲沒有反作用。而容許使用變量的程序設計語言,因爲函數內部的變量狀態不肯定,一樣的輸入,可能獲得不一樣的輸出,所以,這種函數是有反作用的。git
函數式編程的一個特色就是:容許把函數自己做爲參數傳入另外一個函數,同時還容許返回一個函數!github
函數表達式: y = f(x) ---> x = f(x) ---> y = f(f(x))編程
下面寫一個栗子🌰來理解一下: 有這樣一個需求:對於數組[1,2,3,4,5,6,7],首先獲取 > 3的數字,獲取到的數字以後 + 1,再輸出全部數字中的偶數swift
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)
}
}
}
複製代碼
這裏咱們利用行爲式思路解決了這個需求,下面來換個思路來解決,使用 Array
的filter
方法;數組
let array = [1,2,3,4,5,6,7]
array.filter{ $0 > 3}
.filter{ ($0+1) % 2 == 0 }
.forEach { print($0) }
複製代碼
這裏 array.filter
函數接受一個閉包類型的參數,filter
方法會對 array
中的每個元素都用傳入filter
的閉包調用一遍,根據這個閉包的返回值決定是否將這個元素做爲符合條件的元素加入咱們的查找結果中。xcode
簡單來講咱們只須要在傳入的閉包中聲明好查找的規則,這樣咱們就完成整個查找操做的處理了。咱們這裏並無告訴程序應該怎麼去查找知足條件的元素的方法,而只是聲明瞭一個規則。這樣作最大的好處就是可以減小咱們的代碼量,讓咱們的代碼看起來很是的簡潔,並且易理解。 這種方式就是函數式編程的一個例子。bash
響應式編程簡稱RP(Reactive Programming),響應式編程是一種面向數據流和變化傳播的異步編程範式。這意味着能夠在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值經過數據流進行傳播。閉包
簡單的來講就是基於事件流的編程方式。事件流就是將要發生事件按照時間順序排序發生造成的。而當這個事件流產生了返回的數據,能夠馬上獲得通知並調用回調函數去處理數據。(在現實生活中就是:我在家拿起手機預訂了一份外賣,而後商家會收到訂單,開始製做外賣,製做完成後會通知騎手取餐,騎手接單取餐,而後送餐,到達目的地,外賣送達,訂單完成,這樣一系列的事件,先稱它爲事件流,若是在這些事件流進行的過程當中,好比說商家沒有出餐,或者騎手沒有取餐等等,這些都會致使這個事件流沒法完成,而咱們能夠根據這個事件流中的任何一個事件的結果來進行響應)
從這個小例子能夠看到組成響應式編程的三種動做(數據):事件(數據),錯誤,結束。經過獲得這三個響應式動做,咱們就能夠在程序中做出不一樣的響應。
這裏我的以爲有點很重要,就是要對事件進行「預訂/監聽」,而後才能收到這個事件的反饋。而對於我而言,我就是監聽事件的人,也就是「觀察者/訂閱方」,監聽+觀察者是否是就是咱們比較熟悉的觀察者模式,那麼響應式編程就是觀察者模式+事件流的控制。
函數響應式編程 FRP(Functional Reactive Programming)是函數式編程與響應式編程相結合起來的,響應式編程思想爲體, 函數式編程思想爲用。(比較經典的框架就是RAC和RxSwift),經常有人說,FRP能讓你的代碼像數學同樣簡潔,業務像流水同樣清晰流暢。
首先,ReactiveX
(簡寫: Rx
) 是一個能夠幫助咱們簡化異步編程的框架,簡單來講就是基於異步 Event
序列的響應式編程,並提供更優雅的數據綁定,能夠時刻響應新的數據同時順序地處理它們。RxSwift
(ReactiveX for Swift)
,就是ReactiveX
的Swift
版本 ReactiveX
家族很是強大,就如同‘毒液家族’同樣的強大,除了我後面會學習的 RxSwift
以外,還有 RAC(ReactiveCocoa), RxJava, RxJS, RxKotlin, Rx.NET
...等等.
Rx.xcodeproj
拖拽至工程中,Project -> Targets -> General -> Embedded Binaries
配置項, RxSwift.framework
、RxCocoa.framework
添加進來便可RxSwift
的地方import
進來# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'RxSwift', '~> 5.0'
pod 'RxCocoa', '~> 5.0'
end
複製代碼
替換 YOUR_TARGET_NAME
而後在 Podfile
目錄下, 終端輸入:
$ pod install
複製代碼
🗣🗣🗣這裏說明一下爲何會導入
RxSwift
和RxCocoa
兩個庫,它們的做用分別是:
RxSwift
:它只是基於Swift
語言的Rx
標準實現接口庫,因此RxSwift
裏不包含任何Cocoa
或者UI
方面的類。RxCocoa
:是基於RxSwift
針對於iOS
開發的一個庫,它經過Extension
的方法給原生的好比UI
控件添加了Rx
的特性,使得咱們更容易訂閱和響應這些控件的事件。
Rx
就是和複合的代名詞Rx
是徹底經過單元測試的到底是不是這樣的呢,下面經過一些經常使用的方式來驗證一下:
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("響應")
print(change as Any)
}
deinit {
self.removeObserver(self.person, forKeyPath: "name", context: nil)
}
複製代碼
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
寫的代碼更少了,並且也不用去實現觀察者的代理方法,不用關心是否遺漏removeObserver
方法。
func setupButton() {
button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}
@objc func didClickButton(){
print("點我幹什麼")
}
複製代碼
func setupButton() {
self.button.rx.tap
.subscribe(onNext: { () in
print("點擊事件")
})
.disposed(by: disposeBag)
}
複製代碼
不須要實現
Target Action
,代碼更加簡單了
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("contentOffset: \(scrollView.contentOffset)")
}
}
複製代碼
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.rx.contentOffset
.subscribe(onNext: { contentOffset in
print("contentOffset: \(contentOffset)")
})
.disposed(by: disposeBag)
}
}
複製代碼
不須要實現代理方法啦,能夠直接獲取到
scrollview
的偏移
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)
}
複製代碼
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx
.notification(.UIApplicationWillEnterForeground)
.subscribe(onNext: { (notification) in
print("Application Will Enter Foreground")
})
.disposed(by: disposeBag)
}
複製代碼
不須要去關注是否已經移除了通知,不會由於沒有移除通知而出現崩潰
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("點我幹嗎")
}
複製代碼
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)
}
複製代碼
少寫了手勢觸發實現的方法,代碼更簡介了
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")
}
複製代碼
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
更好地是不用考慮頁面上的Scrollview
對timer
的影響
看完這些小例子🌰以後,以爲RxSwift
比通常的Swift
寫法要簡單好多,簡直就是「騷通常的操做」。
在前面的概念介紹中大概已經知道了ReactiveX
是基於異步 Event
序列的響應式編程, 並提供更優雅的數據綁定,能夠時刻響應新的數據同時順序地處理它們,既然要響應數據,就還須要一個觀察者。
RxSwift 核心概念就能夠理解爲一個觀察者(Observer
)訂閱一個可被觀察序列(Observable
)。觀察者對可被觀察序列發射的事件(Event
)或事件序列做出響應。
先來理解一下這三個類的概念:
Observable<T>
這個類就是 Rx
框架的基礎,咱們能夠稱它爲可觀察序列。它的做用就是能夠異步地產生一系列的 Event
(事件),即一個 Observable<T>
對象會隨着時間推移不按期地發出 event(element : T)
這樣一個東西。Event
還能夠攜帶數據,它的泛型 <T>
就是用來指定這個 Event
攜帶的數據的類型。Observer
(訂閱者)來訂閱它,這樣這個訂閱者才能收到 Observable<T>
不時發出的 Event
。既然Observable
是一個可被觀察的序列,能夠異步地產生一系列的 Event
,那麼查看RxSwift/Event.swift
源碼能夠發現
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
可觀察序列能夠發出三種不一樣類型的Event
事件:
next
事件就是那個能夠攜帶數據 <T>
的事件,能夠說它就是一個普通事件error
事件表示一個錯誤,它能夠攜帶具體的錯誤內容,一旦 Observable
發出了 error
event,則這個
Observable就等於終止了,之後它不再會發出
event` 事件了。completed
事件表示 Observable
發出的事件正常地結束了,跟 error
同樣,一旦 Observable
發出了 completed event
,則這個 Observable
就等於終止了,之後它不再會發出 event
事件了序列監聽有三個步驟:1.建立序列,2訂閱序列,3.發送信號。當建立序列,並訂閱了序列後,只要某個事件發送了序列消息,就能夠在序列訂閱的閉包裏面監聽到發送的消息。
下面建立一個可觀察序列來感覺一下:
//第一步:建立序列
//在create()函數中傳入一個閉包,任務是對每個過來的訂閱進行處理
let ob = Observable<Any>.create { (observer) -> Disposable in
// 第三步:發送信號(onCompleted和onError只能發送一個)
observer.onNext("你好啊")
observer.onCompleted()
// observer.onError(NSError.init(domain: "loser", code: 10010, userInfo: nil))
return Disposables.create()
//第二步:訂閱信息
//當咱們訂閱了Observable的消息後,只要Observable的事件觸發,都會經過onNext這個閉包告訴咱們。
let _ = ob.subscribe(onNext: { (text) in
print("訂閱到:\(text)") //這裏會監聽到訂閱的Observable事件
}, onError: { (error) in
print("error: \(error)") //當發生錯誤時,會回調這裏
}, onCompleted: { // 當序列執行完畢時,會回調這裏。
print("完成")
}) {
print("銷燬")
}
.disposed(by: disposeBag)
複製代碼
DisposeBag
:做用是Rx
在視圖控制器或者其持有者將要銷燬的時候,自動釋法掉綁定在它上面的資源。它是經過相似「訂閱處置機制」方式實現(相似於NotificationCenter
的removeObserver
)。
這就算是RxSwift
響應式的核心邏輯了,這裏有點疑惑就是:爲何觀察者發出的信號,可觀察序列可以訂閱到呢?
未完待續...... RxSwift學習--核心邏輯再探
注:感謝下面的參考文檔