之前學了下RAC,在RAC當中的話其實基本都是block,它的中心思想其實包含了KVO和疊代器模式的思想下一步需要做什麼還有就是函數式程式設計。
在RxSwift中有幾個概念比較重要的就是Observer也就是觀察者,而Observerable是可被觀察者,這個是個事件源,我們的觀察者需要去訂閱這個事件源
才會去收到消息的通知。在RxSwift中會把每個事件源都去看作是一個序列(sequence)。
下面我們會去進行調用最多的就是Observable.方法其實就是去建立一個事件序列,然後我們去訂閱它。
下面介紹下just方法其實就是去建立一個sequence,然後這個sequence中隻會有一個事件,這個事件中包裹的内容也就是我們傳進入的hello world,下面就會列印出hello world,而且會自動的去做完成操作
let disposeBag = DisposeBag()
let justSequence = Observable.just("hello world")
justSequence.subscribe(onNext: {
print($0)
}).disposed(by: DisposeBag)
因為我們在去使用這個訂閱方法的時候,也就是說會先去發送next,再去發送completed
let disposeBag = DisposeBag()
let justSequence = Observable.just("hello world")
justSequence.subscribe { (event) in
print(event)
}
self.justSequence = justSequence
會輸出的是
緊接着我們來看個東西,一個枚舉值,這裡其實就是說next就是序列中的新元素産生的,會去處理,error就是序列中的元素發生錯誤,處理錯誤,結束整個序列,對所有的觀察者取消訂閱,其實這裡的元素可以了解為信号,complete标記整個序列已經完成,取消所有觀察者的訂閱,一個序列發送 completed 或者 error 事件時,不會再去産生新的信号了,
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
}
下面還需要說一下在Swift中我們不需要去寫出enum的實際名稱也就是case Event.next:print(value)。因為類型檢查器能夠自動為此進行類型推算,在下面其實我們就是通過一個集合去建立一個序列.
let fromSequence = Observable.from(["1","2","3","4"])
fromSequence.subscribe { (event) in
switch event{
case .next(let value):
print(value)
case .error(let error):
print(error)
case .completed:
print("完成")
}
}
列印如下
其中上面調用的是on:Event訂閱所有的内容了就是說不管發生了錯誤也好還是完成了也好我都會去監聽,去做處理
接下來我們還可以自己去建立一個自定義序列,這裡把可被觀察者給建立出來了,然後這個函數接收觀察者作為其的參數
_ = Observable<String>.create({ observer in
observer.on(.next("哈哈"))
observer.on(.completed)
return Disposables.create()
}).subscribe{ x in
print(x)
}
我們可以看看create的,我們傳入的observer應該就是在RxSwift.AnyObserver<Self.E>這裡面做了實作,可以看到最後傳回的是一個可被觀察者Observable
public static func create(_ subscribe: @escaping (RxSwift.AnyObserver<Self.E>) -> Disposable) -> RxSwift.Observable<Self.E>
再簡單的介紹下Subject,Subject即可以是Observable也可以是一個Observer,也就是說它是一個既可以去發送事件也可以去監聽事件。
PublishSubject:訂閱PublishSubject的時候,隻能接收到訂閱它之後發生的事件,前面發送的事件是不會接受的
let publishSubject = PublishSubject<Any>()
publishSubject.subscribe { (value) in
print(value)
}
//建立序列中一個事件,這個事件的内容是Hello
publishSubject.onNext("Hello")
publishSubject.onNext("world")
publishSubject.onNext("world")
ReplaySubject:訂閱ReplaySubject的時候,可以接收到訂閱它之後的事件,也可以接受訂閱它之前發出的事件,接受幾個事件取決與bufferSize的大小,下面就是隻去接收前面兩個事件
let replySubject = ReplaySubject<String>.create(bufferSize: 2)
replySubject.onNext("1")
replySubject.onNext("2")
replySubject.onNext("3")
replySubject.subscribe { (value) in
print("列印的值為\(value)")
}
replySubject.onNext("4")
replySubject.onNext("5")
BehaviorSubject:訂閱了BehaviorSubject,預設情況下訂閱者隻能接受前面的最新的一條消息,而後面的都可以收到,這裡的BehaviorSubject其實也就是為了防止第一個訂閱者沒有一個前一個信号可以接收而設立的
let behaviorSubject = BehaviorSubject(value: "A")
behaviorSubject.onNext("E")
behaviorSubject.onNext("F")
behaviorSubject.subscribe({
print($0)
})
behaviorSubject.onNext("B")
behaviorSubject.onNext("C")
behaviorSubject.subscribe({
print("2----\($0)")
})
behaviorSubject.onNext("D")
behaviorSubject.onNext("H")
再記錄一個Variable
Variable可以解釋成是一個可監聽的資料結構。使用Variable,可以監聽資料的變化,當Variable被釋放的時候,它會完成通過
asObservable
()所建立的序列,其實Variable裡面是封裝了BehaviorSubject的
Variable的使用,其實可以看出和BehaviorSubject一樣都是隻接收了上面的最新的一個信号,而後面的都是全部接收的,以及在通過asObservable()建立的序列釋放的時候會去發送completed信号,因為當我們把variable設為全局屬性的時候是沒有去列印complete的
let variable = Variable(0)
variable.value=1;
variable.value=2;
let observable = variable.asObservable()
observable.subscribe(onNext: { (value) in
print("1、列印的值為\(value)")
}) {
print("complete")
}
variable.value=3;
variable.value=4;
variable.value=5;
observable.subscribe(onNext: { (value) in
print("2、列印的值為\(value)")
}) {
print("complete")
}
variable.value=6;
variable.value=7;
一般我們在開發的時候都會去建立一個全局的DisposeBag,就比如說我們可以把它引用為我們的屬性,Disposebags 的作用跟ARC類似。當一個 Disposebags 被釋放時,它會對内部每一個可以被清理的元素調用 dispose。DisposeBag 沒有 dispose 方法,是以我們肯定不能去手動的調用。如果我們需要立即的清理資源,可以建立一個新的DisposeBag去替換掉我們之前強引用的那個Disposebags。用到這個主要想在事件完成之後去取消訂閱,減少記憶體的使用和性能上面的消耗
一行代碼可以綁定textField的text和label的text,這裡的map其實就和RAC中的map是相似的,我不會立刻給你資料,我可以在裡面先做完操作再給你。
textField.rx.text.map{
return "? \($0!)"
}
.bind(to:helloLabel.rx.text).disposed(by: disposeBag)
關于throttle和debounce這兩個其實都是去做了限制事件的接受為什麼這麼說?就拿上面的舉例子,如果我們不想讓文本框一改變上面的label就緊跟着改變的話,其實我們就可以用到這兩個中的其中一個,這兩個都可以去限制在某個時間間隔内我不去接受你文本框改變所産生的那個事件。
再結合一個小案例,就是通過在文本框輸入點選送出按鈕之後可以把文本框裡面的每次送出内容都插入label中。
//建立一個可監聽的數組,其實這裡用普通的可變數組也是可以的
let nameArray: Variable<[String]> = Variable([])
submitBtn.rx.tap.subscribe(onNext:{
if self.textField.text != ""
{
//把textField中的資料放入這個nameArray中
self.nameArray.value.append(self.textField.text!)
//将數組中的元素以,來分隔開來,調用下面的joined方法的元素必須是String
self.namesLabel.rx.text.onNext(self.nameArray.value.joined(separator: ","))
print(self.nameArray.value.joined(separator: ","))
//将文本框裡面的内容清空
self.textField.rx.text.onNext("")
}
}).disposed(by: disposeBag)
首先看控制台列印也就是我們上面輸出的數組經過插入 , 之後的内容
其中這裡有一個注意點就是joined(separator: ",")這個方法,隻能是數組裡面的元素是String的時候才能使用,因為我們點進去看會看到下面的内容,這裡面的Element==String就表示的是數組裡面的元素的類型必須要是String
extension Array where Element == String {
/// Returns a new string by concatenating the elements of the sequence,
/// adding the given separator between each element.
///
/// The following example shows how an array of strings can be joined to a
/// single, comma-separated string:
///
/// let cast = ["Vivien", "Marlon", "Kim", "Karl"]
/// let list = cast.joined(separator: ", ")
/// print(list)
/// // Prints "Vivien, Marlon, Kim, Karl"
///
/// - Parameter separator: A string to insert between each of the elements
/// in this sequence. The default separator is an empty string.
/// - Returns: A single, concatenated string.
public func joined(separator: String = default) -> String
}
當然我們綁定textField的文本到label上面的文本,還有另外一種方式就是不經過map的這裡為什麼要
extension ControlPropertyType where Self.E == String? {
/// Transforms control property of type `String?` into control property of type `String`.
public var orEmpty: RxCocoa.ControlProperty<String> { get }
}
通過orEmpty?是因為其實就這裡面将String?轉為String處理,這樣的話我們就不需要考慮String為nil的情況。在Rxswift中,對于所有字元串的監聽都是轉為orEmpty處理的
self.textField1.rx.text.orEmpty.bind(to:self.inputLabel.rx.text)
關于orEmpty其實架構裡面也略微有解釋
extension ControlPropertyType where Self.E == String? {
/// Transforms control property of type `String?` into control property of type `String`.
public var orEmpty: RxCocoa.ControlProperty<String> { get }
}
這裡為什麼可以綁定,其實我們用到的方法是bind(to: ObserverType)這個方法,其中ObserverType是一個協定,而我們的self.inputLabel.rx.text實際上是這個類型Binder<String?>
發在我們再點進去看,我們會發現它實作了ObserverType的協定,而實作ObserverType協定的是觀察者對象,用于觀察Observable發出的信号。是以我們這個label的就可以監聽到textField信号的發出,然後去進行改變自己的值
還有一個那就是為什麼這個textField是可被UILabel的rx.text觀察的?而且它還可以被訂閱。例如下面可以對它進行訂閱
self.textField1.rx.text.subscribe(onNext: { (value) in
print("1、列印的值為\(value!)")
}) {
print("complete")
}
之是以可以去訂閱它的原因其實是因為當我們去看UITextField+Rx的檔案的時候會發現這個rx.text其實是一個ControlProperty<String?>類型的
之後我們再往ControlProperty<String?>點進去看會發現是個結構體,并且遵守了ControlPropertyType協定
再點進ControlPropertyType協定去看,發現其也繼承了ObservableType協定和ObserverType協定,而上文提到的ControlProperty其實是一個實作ControlPropertyType的結構體,是以我們可以将ControlProperty的對象當作是一個Observable來使用,也就是一個可被觀察者
介紹下never,這個never就是建立出的Sequence是不會發出任何信号的,我們有的時候可以把它和其他信号進行concat,這樣也能起到屏蔽信号的作用
let neverSequence = Observable<Any>.never()
neverSequence.subscribe { (_) in
print("什麼都不會發生")
}.disposed(by: disposeBag)
empty,建立一個空的sequence,隻會去發送一個completed的事件,在下面我們可以看到列印的value都是completed,這裡應用場景的話可能會用于使用者做了某件事情你隻需要告訴它結果,就比如說下單了,訂單完成還是沒完成。
let emptySequence = Observable<Any>.empty()
emptySequence.subscribe { (value) in
print(value)
}.disposed(by: disposeBag)
emptySequence.subscribe { (value) in
print(value)
}.disposed(by: disposeBag)
of就是建立出一個sequence,裡面有很多事件信号,順序發送出去,發送完成之後,然後再發送completed。這裡of和from的差別其實就在于of是把它括号裡面的1 2 3分别當做單獨的事件,一個一個的拿出來發送,而from我們括号裡面傳入的是集合類,其實就是解析事件裡面的元素,再把元素給一個個的去拿出來。
let ofSequence = Observable.of(1,2,3)
ofSequence.subscribe { (event) in
print(event)
}
error,建立了一個可觀察的序列,隻發出error事件并結束,其他事件不會發出
enum hhError:Error{
case test
}
Observable<Error>.error(hhError.test).subscribe({ (event) in
print(event)
})
輸出為
這裡再簡單的介紹下Driver,Driver是驅動者,司機的意思,我們其實可以去了解為一個驅動者,在功能上它和被觀察者Observable是類似的,它是可以和被觀察者進行互相轉換的,它會驅動着一個觀察者,當它的序列中有事件的時候,被它驅動着的觀察者就能進行相應的操作。一般我們會将一個Observable被觀察者轉換成Driver後再進行driver其實也就是開車操作做具體的事情,比如說綁定将某個值綁定到另一個值上面去。
它不會去直接的發送Error事件
它的觀察訂閱是發生在主線程(UI線程)的,比如說我們将一個UI控件的值綁定到另外一個UI控件,用drive是預設在主線程上執行的,不然的話我們調用綁定方法的時候如果再子線程上執行,那麼在更新UI的時候是會出現問題的
本身就有使用shareReplayLatestWhileConnected,保證隻被訂閱一次,
Observable有個隐式的規定,就是在一個信号處理完成之前,不會發送下一個信号