RxSwift (二)序列核心逻辑分析github
RxSwift (三)Observable的建立,订阅,销毁编程
RxSwift(四)高阶函数json
RxSwift(五)(Rxswift对比swift,oc用法)swift
RxSwift (十) 基础使用篇 1- 序列,订阅,销毁bash
RxSwift学习之十二 (基础使用篇 3- UI控件扩展)markdown
1)在编写代码时咱们常常会须要检测某些值的变化(好比:textFiled 输入值的变化、数据请求完成或失败的变化),而后进行相应的处理。 过去针对不一样的状况,咱们须要采用不一样的事件传递方法去处理,好比:delegate、notification、target-action、KVO 等等。 而 RectiveX 机制(由 RxSwift 实现)的出现,让程序里的事件传递响应方法作到统一。将以前那些经常使用的事件传递方法(好比:delegate、notification、target-action 等等),所有替换成 Rx 的“信号链”方式。 (2)若是咱们平时使用的是 MVVM 开发模式的话,经过 RxSwift 能够得到更加方便的数据绑定的方法,使得 MVVM 开发更加如虎添翼。网络
RxSwift:它只是基于 Swift 语言的 Rx 标准实现接口库,因此 RxSwift 里不包含任何 Cocoa 或者 UI 方面的类。 RxCocoa:是基于 RxSwift 针对于 iOS 开发的一个库,它经过 Extension 的方法给原生的好比 UI 控件添加了 Rx 的特性,使得咱们更容易订阅和响应这些控件的事件。
import UIKit //歌曲结构体 struct Music { let name: String //歌名 let singer: String //演唱者 init(name: String, singer: String) { self.name = name self.singer = singer } } //实现 CustomStringConvertible 协议,方便输出调试 extension Music: CustomStringConvertible { var description: String { return "name:\(name) singer:\(singer)" } } 复制代码
import Foundation //歌曲列表数据源 struct MusicListViewModel { let data = [ Music(name: "无条件", singer: "陈奕迅"), Music(name: "你曾是少年", singer: "S.H.E"), Music(name: "从前的我", singer: "陈洁仪"), Music(name: "在木星", singer: "朴树"), ] } 复制代码
import UIKit import RxSwift class ViewController: UIViewController { //tableView对象 @IBOutlet weak var tableView: UITableView! //歌曲列表数据源 let musicListViewModel = MusicListViewModel() override func viewDidLoad() { super.viewDidLoad() //设置代理 tableView.dataSource = self tableView.delegate = self } } extension ViewController: UITableViewDataSource { //返回单元格数量 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return musicListViewModel.data.count } //返回对应的单元格 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell")! let music = musicListViewModel.data[indexPath.row] cell.textLabel?.text = music.name cell.detailTextLabel?.text = music.singer return cell } } extension ViewController: UITableViewDelegate { //单元格点击 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("你选中的歌曲信息【\(musicListViewModel.data[indexPath.row])】") } } 复制代码
ViewModel
作些修改
Observable Squence
),而对象当中的内容和咱们以前在数组当中所包含的内容是彻底同样的。Subscribe
)”,有点相似于“通知(NotificationCenter
)”import RxSwift //歌曲列表数据源 struct MusicListViewModel { let data = Observable.just([ Music(name: "无条件", singer: "陈奕迅"), Music(name: "你曾是少年", singer: "S.H.E"), Music(name: "从前的我", singer: "陈洁仪"), Music(name: "在木星", singer: "朴树"), ]) } 复制代码
ViewController.swift
)
代码的简单说明:
DisposeBag
:做用是 Rx 在视图控制器或者其持有者将要销毁的时候,自动释法掉绑定在它上面的资源。它是经过相似“订阅处置机制”方式实现(相似于 NotificationCenter 的 removeObserver)。rx.items(cellIdentifier:
):这是 Rx 基于 cellForRowAt 数据源方法的一个封装。传统方式中咱们还要有个 numberOfRowsInSection 方法,使用 Rx 后就再也不须要了(Rx 已经帮咱们完成了相关工做)。rx.modelSelected
: 这是 Rx 基于 UITableView 委托回调方法 didSelectRowAt 的一个封装。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { //tableView对象 @IBOutlet weak var tableView: UITableView! //歌曲列表数据源 let musicListViewModel = MusicListViewModel() //负责对象销毁 let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() //将数据源数据绑定到tableView上 musicListViewModel.data .bind(to: tableView.rx.items(cellIdentifier:"musicCell")) { _, music, cell in cell.textLabel?.text = music.name cell.detailTextLabel?.text = music.singer }.disposed(by: disposeBag) //tableView点击响应 tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in print("你选中的歌曲信息【\(music)】") }).disposed(by: disposeBag) } } 复制代码
Observable 做为 Rx 的根基,咱们首先对它要有一些基本的了解。
Observable:
Observable 这个类就是 Rx 框架的基础,咱们能够称它为可观察序列。它的做用就是能够异步地产生一系列的 Event(事件),即一个 Observable 对象会随着时间推移不按期地发出 event(element : T) 这样一个东西。 并且这些 Event 还能够携带数据,它的泛型 就是用来指定这个 Event 携带的数据的类型。 有了可观察序列,咱们还须要有一个 Observer(订阅者)来订阅它,这样这个订阅者才能收到 Observable 不时发出的 Event。
Event
查看 RxSwift 源码能够发现,事件 Event 的定义以下:
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 } 复制代码
next
事件就是那个能够携带数据 的事件,能够说它就是一个“最正常”的事件。error
事件表示一个错误,它能够携带具体的错误内容,一旦 Observable
发出了 error event
,则这个 Observable
就等于终止了,之后它不再会发出 event 事件了。completed
事件表示 Observable
发出的事件正常地结束了,跟 error 同样,一旦 Observable
发出了 completed event
,则这个 Observable
就等于终止了,之后它不再会发出 event 事件了。Observable
的实例想象成于一个 Swift 中的 Sequence:
Observable
(ObservableType
)至关于一个序列 Sequence
(SequenceType
)。ObservableType.subscribe(_:)
方法其实就至关于 SequenceType.generate()
SequenceType
是同步的循环,而 Observable
是异步的。Observable
对象会在有任何 Event 时候,自动将 Event 做为一个参数经过 ObservableType.subscribe(_:)
发出,并不须要使用 next 方法。(1)该方法经过传入一个默认值来初始化。 (2)下面样例咱们显式地标注出了 observable 的类型为 Observable,即指定了这个 Observable 所发出的事件携带的数据类型必须是 Int 类型的。
let observable = Observable<Int>.just(5) 复制代码
(1)该方法能够接受可变数量的参数(必须要是同类型的) (2)下面样例中我没有显式地声明出 Observable 的泛型类型,Swift 也会自动推断类型。
let observable = Observable.of("A", "B", "C") 复制代码
(1)该方法须要一个数组参数。 (2)下面样例中数据里的元素就会被当作这个 Observable 所发出 event 携带的数据内容,最终效果同上面饿 of() 样例是同样的。
let observable = Observable.from(["A", "B", "C"]) 复制代码
let observable = Observable<Int>.never() 复制代码
let observable = Observable<Int>.never() 复制代码
enum MyError: Error { case A case B } let observable = Observable<Int>.error(MyError.A) 复制代码
(1)该方法经过指定起始和结束数值,建立一个以这个范围内全部值做为初始值的 Observable 序列。 (2)下面样例中,两种方法建立的 Observable 序列都是同样的。
//使用range() let observable = Observable.range(start: 1, count: 5) //使用of() let observable = Observable.of(1, 2, 3 ,4 ,5) 复制代码
let observable = Observable.repeatElement(1) 复制代码
(1)该方法建立一个只有当提供的全部的判断条件都为 true 的时候,才会给出动做的 Observable 序列。 (2)下面样例中,两种方法建立的 Observable 序列都是同样的。
//使用generate()方法 let observable = Observable.generate( initialState: 0, condition: { $0 <= 10 }, iterate: { $0 + 2 } ) //使用of()方法 let observable = Observable.of(0 , 2 ,4 ,6 ,8 ,10) 复制代码
(1)该方法接受一个 block 形式的参数,任务是对每个过来的订阅进行处理。 (2)下面是一个简单的样例。为方便演示,这里增长了订阅相关代码
//这个block有一个回调参数observer就是订阅这个Observable对象的订阅者 //当一个订阅者订阅这个Observable对象的时候,就会将订阅者做为参数传入这个block来执行一些内容 let observable = Observable<String>.create{observer in //对订阅者发出了.next事件,且携带了一个数据"hangge.com" observer.onNext("hangge.com") //对订阅者发出了.completed事件 observer.onCompleted() //由于一个订阅行为会有一个Disposable类型的返回值,因此在结尾必定要returen一个Disposable return Disposables.create() } //订阅测试 observable.subscribe { print($0) } 复制代码
(1)该个方法至关因而建立一个 Observable 工厂,经过传入一个 block 来执行延迟 Observable 序列建立的行为,而这个 block 里就是真正的实例化序列对象的地方。 (2)下面是一个简单的演示样例:
//用于标记是奇数、仍是偶数 var isOdd = true //使用deferred()方法延迟Observable序列的初始化,经过传入的block来实现Observable序列的初始化而且返回。 let factory : Observable<Int> = Observable.deferred { //让每次执行这个block时候都会让奇、偶数进行交替 isOdd = !isOdd //根据isOdd参数,决定建立并返回的是奇数Observable、仍是偶数Observable if isOdd { return Observable.of(1, 3, 5 ,7) }else { return Observable.of(2, 4, 6, 8) } } //第1次订阅测试 factory.subscribe { event in print("\(isOdd)", event) } //第2次订阅测试 factory.subscribe { event in print("\(isOdd)", event) } 复制代码
运行结果以下,能够看到咱们两次订阅的获得的 Observable 是不同的:
(1)这个方法建立的 Observable 序列每隔一段设定的时间,会发出一个索引数的元素。并且它会一直发送下去。 (2)下面方法让其每 1 秒发送一次,而且是在主线程(MainScheduler)发送。
let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 复制代码
//5秒种后发出惟一的一个元素0 let observable = Observable<Int>.timer(5, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 复制代码
//延时5秒种后,每隔1秒钟发出一个元素 let observable = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance) observable.subscribe { event in print(event) } 复制代码
let observable = Observable.of("A", "B", "C") observable.subscribe { event in print(event) } 复制代码
运行结果以下,能够看到:
初始化 Observable 序列时设置的默认值都按顺序经过 .next 事件发送出来。 当 Observable 序列的初始数据都发送完毕,它还会自动发一个 .completed 事件出来。
let observable = Observable.of("A", "B", "C") observable.subscribe { event in print(event.element) } 复制代码
运行结果以下:
subscribe
方法,它能够把 event 进行分类:
let observable = Observable.of("A", "B", "C") observable.subscribe(onNext: { element in print(element) }, onError: { error in print(error) }, onCompleted: { print("completed") }, onDisposed: { print("disposed") }) 复制代码
运行结果以下:
subscribe()
方法的 onNext
、onError
、onCompleted
和 onDisposed
这四个回调 block 参数都是有默认值的,即它们都是可选的。因此咱们也能够只处理 onNext
而无论其余的状况。let observable = Observable.of("A", "B", "C") observable.subscribe(onNext: { element in print(element) }) 复制代码
运行结果以下: A B C
doOn 介绍
(1)咱们可使用 doOn 方法来监听事件的生命周期,它会在每一次事件发送前被调用。 (2)同时它和 subscribe 同样,能够经过不一样的 block 回调处理不一样类型的 event。好比: do(onNext:) 方法就是在 subscribe(onNext:) 前调用 而 do(onCompleted:) 方法则会在 subscribe(onCompleted:) 前面调用。
使用样例
let observable = Observable.of("A", "B", "C") observable .do(onNext: { element in print("Intercepted Next:", element) }, onError: { error in print("Intercepted Error:", error) }, onCompleted: { print("Intercepted Completed") }, onDispose: { print("Intercepted Disposed") }) .subscribe(onNext: { element in print(element) }, onError: { error in print(error) }, onCompleted: { print("completed") }, onDisposed: { print("disposed") }) 复制代码
Observable
序列被建立出来后它不会立刻就开始被激活从而发出 Event,而是要等到它被某我的订阅了才会激活它。Observable
序列激活以后要一直等到它发出了 .error
或者 .completed
的 event
后,它才被终结。-(1)使用该方法咱们能够手动取消一个订阅行为。 -(2)若是咱们以为这个订阅结束了再也不须要了,就能够调用 dispose()
方法把这个订阅给销毁掉,防止内存泄漏。 -(3)当一个订阅行为被 dispose
了,那么以后 observable
若是再发出 event
,这个已经 dispose
的订阅就收不到消息了。下面是一个简单的使用样例。
let observable = Observable.of("A", "B", "C") //使用subscription常量存储这个订阅方法 let subscription = observable.subscribe { event in print(event) } //调用这个订阅的dispose()方法 subscription.dispose() 复制代码
- 咱们能够把一个 DisposeBag 对象当作一个垃圾袋,把用过的订阅行为都放进去。
- 而这个 DisposeBag 就会在本身快要 dealloc 的时候,对它里面的全部订阅行为都调用 dispose() 方法。
let disposeBag = DisposeBag() //第1个Observable,及其订阅 let observable1 = Observable.of("A", "B", "C") observable1.subscribe { event in print(event) }.disposed(by: disposeBag) //第2个Observable,及其订阅 let observable2 = Observable.of(1, 2, 3) observable2.subscribe { event in print(event) }.disposed(by: disposeBag) 复制代码
(1)建立观察者最直接的方法就是在 Observable 的 subscribe 方法后面描述当事件发生时,须要如何作出响应。 (2)好比下面的样例,观察者就是由后面的 onNext,onError,onCompleted 这些闭包构建出来的。
let observable = Observable.of("A", "B", "C") observable.subscribe(onNext: { element in print(element) }, onError: { error in print(error) }, onCompleted: { print("completed") }) 复制代码
运行结果:
//Observable序列(每隔1秒钟发出一个索引数) let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable .map { "当前索引数:\($0 )"} .bind { [weak self](text) in //收到发出的索引数后显示到label上 self?.label.text = text } .disposed(by: disposeBag) 复制代码
//观察者 let observer: AnyObserver<String> = AnyObserver { (event) in switch event { case .next(let data): print(data) case .error(let error): print(error) case .completed: print("completed") } } let observable = Observable.of("A", "B", "C") observable.subscribe(observer) 复制代码
//观察者 let observer: AnyObserver<String> = AnyObserver { [weak self] (event) in switch event { case .next(let text): //收到发出的索引数后显示到label上 self?.label.text = text default: break } } //Observable序列(每隔1秒钟发出一个索引数) let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable .map { "当前索引数:\($0 )"} .bind(to: observer) .disposed(by: disposeBag) } 复制代码
(1)相较于 AnyObserver 的大而全,Binder 更专一于特定的场景。Binder 主要有如下两个特征: 不会处理错误事件 确保绑定都是在给定 Scheduler 上执行(默认 MainScheduler) (2)一旦产生错误事件,在调试环境下将执行 fatalError,在发布环境下将打印错误信息。
//观察者 let observer: Binder<String> = Binder(label) { (view, text) in //收到发出的索引数后显示到label上 view.text = text } //Observable序列(每隔1秒钟发出一个索引数) let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable .map { "当前索引数:\($0 )"} .bind(to: observer) .disposed(by: disposeBag) //Observable序列(每隔1秒钟发出一个索引数) let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable .map { $0 % 2 == 0 } .bind(to: button.rx.isEnabled) .disposed(by: disposeBag) 复制代码
有时咱们想让 UI 控件建立出来后默认就有一些观察者,而没必要每次都为它们单独去建立观察者。好比咱们想要让全部的 UIlabel 都有个 fontSize 可绑定属性,它会根据事件值自动改变标签的字体大小。
经过对 UI 类进行扩展 这里咱们经过对 UILabel 进行扩展,增长了一个 fontSize 可绑定属性。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { @IBOutlet weak var label: UILabel! let disposeBag = DisposeBag() override func viewDidLoad() { //Observable序列(每隔0.5秒钟发出一个索引数) let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance) observable .map { CGFloat($0) } .bind(to: label.fontSize) //根据索引数不断变放大字体 .disposed(by: disposeBag) } } extension UILabel { public var fontSize: Binder<CGFloat> { return Binder(self) { label, fontSize in label.font = UIFont.systemFont(ofSize: fontSize) } } } 复制代码
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { @IBOutlet weak var label: UILabel! let disposeBag = DisposeBag() override func viewDidLoad() { //Observable序列(每隔0.5秒钟发出一个索引数) let observable = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance) observable .map { CGFloat($0) } .bind(to: label.rx.fontSize) //根据索引数不断变放大字体 .disposed(by: disposeBag) } } extension Reactive where Base: UILabel { public var fontSize: Binder<CGFloat> { return Binder(self.base) { label, fontSize in label.font = UIFont.systemFont(ofSize: fontSize) } } } 复制代码
其实 RxSwift 已经为咱们提供许多经常使用的可绑定属性。好比 UILabel 就有 text 和 attributedText 这两个可绑定属性。
extension Reactive where Base: UILabel { /// Bindable sink for `text` property. public var text: Binder<String?> { return Binder(self.base) { label, text in label.text = text } } /// Bindable sink for `attributedText` property. public var attributedText: Binder<NSAttributedString?> { return Binder(self.base) { label, text in label.attributedText = text } } } 复制代码
那么上文那个定时显示索引数的样例,咱们其实不须要自定义 UI 观察者,直接使用 RxSwift 提供的绑定属性便可。
//Observable序列(每隔1秒钟发出一个索引数) let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance) observable .map { "当前索引数:\($0 )"} .bind(to: label.rx.text) //收到发出的索引数后显示到label上 .disposed(by: disposeBag) 复制代码
当咱们建立一个 Observable 的时候就要预先将要发出的数据都准备好,等到有人订阅它时再将数据经过 Event 发出去。 但有时咱们但愿 Observable 在运行时能动态地“得到”或者说“产生”出一个新的数据,再经过 Event 发送出去。好比:订阅一个输入框的输入内容,当用户每输入一个字后,这个输入框关联的 Observable 就会发出一个带有输入内容的 Event,通知给全部订阅者。 这个就可使用下面将要介绍的 Subjects 来实现。
-(1)Subjects 既是订阅者,也是 Observable: 说它是订阅者,是由于它可以动态地接收新的值。 说它又是一个 Observable,是由于当 Subjects 有了新的值以后,就会经过 Event 将新值发出给他的全部订阅者。
-(2)一共有四种 Subjects,分别为:PublishSubject
、BehaviorSubject
、ReplaySubject
、Variable
。他们之间既有各自的特色,也有相同之处:
- 首先他们都是
Observable
,他们的订阅者都能收到他们发出的新的Event
。- 直到
Subject
发出.complete
或者.error
的Event
后,该Subject
便终结了,同时它也就不会再发出.next
事件。- 对于那些在
Subject
终结后再订阅他的订阅者,也能收到subject
发出的一条.complete
或.error
的event
,告诉这个新的订阅者它已经终结了。- .他们之间最大的区别只是在于:当一个新的订阅者刚订阅它的时候,能不能收到
Subject
之前发出过的旧Event
,若是能的话又能收到多少个。
-(3)Subject 经常使用的几个方法:
onNext(:)
:是 on(.next(:)) 的简便写法。该方法至关于 subject 接收到一个 .next 事件。onError(:)
:是 on(.error(:)) 的简便写法。该方法至关于 subject 接收到一个 .error 事件。onCompleted()
:是 on(.completed) 的简便写法。该方法至关于 subject 接收到一个 .completed 事件。
PublishSubject
是最普通的Subject
,它不须要初始值就能建立。PublishSubject
的订阅者从他们开始订阅的时间点起,能够收到订阅后Subject
发出的新Event
,而不会收到他们在订阅前已发出的Event
。
PublishSubject
。 下面两条分别表示两个新的订阅,它们订阅的时间点不一样,能够发现 PublishSubject
的订阅者只能收到他们订阅后的 Event
。let disposeBag = DisposeBag() //建立一个PublishSubject let subject = PublishSubject<String>() //因为当前没有任何订阅者,因此这条信息不会输出到控制台 subject.onNext("111") //第1次订阅subject subject.subscribe(onNext: { string in print("第1次订阅:", string) }, onCompleted:{ print("第1次订阅:onCompleted") }).disposed(by: disposeBag) //当前有1个订阅,则该信息会输出到控制台 subject.onNext("222") //第2次订阅subject subject.subscribe(onNext: { string in print("第2次订阅:", string) }, onCompleted:{ print("第2次订阅:onCompleted") }).disposed(by: disposeBag) //当前有2个订阅,则该信息会输出到控制台 subject.onNext("333") //让subject结束 subject.onCompleted() //subject完成后会发出.next事件了。 subject.onNext("444") //subject完成后它的全部订阅(包括结束后的订阅),都能收到subject的.completed事件, subject.subscribe(onNext: { string in print("第3次订阅:", string) }, onCompleted:{ print("第3次订阅:onCompleted") }).disposed(by: disposeBag) 复制代码
运行结果:
-(1)基本介绍
BehaviorSubject
须要经过一个默认初始值来建立。 当一个订阅者来订阅它的时候,这个订阅者会当即收到BehaviorSubjects
上一个发出的event
。以后就跟正常的状况同样,它也会接收到BehaviorSubject
以后发出的新的event
。
-(2)时序图 以下图:最上面一条是 BehaviorSubject
。 下面两条分别表示两个新的订阅,它们订阅的时间点不一样,能够发现 BehaviorSubject
的订阅者一开始就能收到 BehaviorSubjects
以前发出的一个 Event
。
let disposeBag = DisposeBag() //建立一个BehaviorSubject let subject = BehaviorSubject(value: "111") //第1次订阅subject subject.subscribe { event in print("第1次订阅:", event) }.disposed(by: disposeBag) //发送next事件 subject.onNext("222") //发送error事件 subject.onError(NSError(domain: "local", code: 0, userInfo: nil)) //第2次订阅subject subject.subscribe { event in print("第2次订阅:", event) }.disposed(by: disposeBag) 复制代码
运行结果:
ReplaySubject
在建立时候须要设置一个bufferSize
,表示它对于它发送过的 event 的缓存个数。- 好比一个
ReplaySubject
的bufferSize
设置为 2,它发出了 3 个.next
的event
,那么它会将后两个(最近的两个)event
给缓存起来。此时若是有一个subscriber
订阅了这个ReplaySubject
,那么这个subscriber
就会当即收到前面缓存的两个.next
的event
。- 若是一个
subscriber
订阅已经结束的ReplaySubject
,除了会收到缓存的.next
的event
外,还会收到那个终结的.error
或者.complete
的event
。
ReplaySubject
(bufferSize
设为为 2)。 下面两条分别表示两个新的订阅,它们订阅的时间点不一样。能够发现 ReplaySubject
的订阅者一开始就能收到 ReplaySubject
以前发出的两个 Event
(若是有的话)。let disposeBag = DisposeBag() //建立一个bufferSize为2的ReplaySubject let subject = ReplaySubject<String>.create(bufferSize: 2) //连续发送3个next事件 subject.onNext("111") subject.onNext("222") subject.onNext("333") //第1次订阅subject subject.subscribe { event in print("第1次订阅:", event) }.disposed(by: disposeBag) //再发送1个next事件 subject.onNext("444") //第2次订阅subject subject.subscribe { event in print("第2次订阅:", event) }.disposed(by: disposeBag) //让subject结束 subject.onCompleted() //第3次订阅subject subject.subscribe { event in print("第3次订阅:", event) }.disposed(by: disposeBag) 复制代码
运行结果:
-(1)基本介绍
Variable
其实就是对BehaviorSubject
的封装,因此它也必需要经过一个默认的初始值进行建立。Variable
具备BehaviorSubject
的功能,可以向它的订阅者发出上一个event
以及以后新建立的event
。- 不一样的是,
Variable
还把会把当前发出的值保存为本身的状态。同时它会在销毁时自动发送.complete
的event
,不须要也不能手动给Variables
发送completed
或者error
事件来结束它。- 简单地说就是
Variable
有一个value
属性,咱们改变这个value
属性的值就至关于调用通常Subjects
的onNext()
方法,而这个最新的onNext()
的值就被保存在value
属性里了,直到咱们再次修改它。Variables
自己没有subscribe()
方法,可是全部Subjects
都有一个asObservable()
方法。咱们可使用这个方法返回这个Variable
的Observable
类型,拿到这个Observable
类型咱们就能订阅它了。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let disposeBag = DisposeBag() //建立一个初始值为111的Variable let variable = Variable("111") //修改value值 variable.value = "222" //第1次订阅 variable.asObservable().subscribe { print("第1次订阅:", $0) }.disposed(by: disposeBag) //修改value值 variable.value = "333" //第2次订阅 variable.asObservable().subscribe { print("第2次订阅:", $0) }.disposed(by: disposeBag) //修改value值 variable.value = "444" } } 注意:因为 Variable 对象在 viewDidLoad() 方法内初始化,因此它的生命周期就被限制在该方法内。当这个方法执行完毕后,这个 Variable 对象就会被销毁,同时它也就自动地向它的全部订阅者发出 .completed 事件 复制代码
运行结果:
BehaviorRelay
是做为Variable
的替代者出现的。它的本质其实也是对BehaviorSubject
的封装,因此它也必需要经过一个默认的初始值进行建立。BehaviorRelay
具备BehaviorSubject
的功能,可以向它的订阅者发出上一个event
以及以后新建立的event
。- 与
BehaviorSubject
不一样的是,不须要也不能手动给BehaviorReply
发送completed
或者error
事件来结束它(BehaviorRelay
会在销毁时也不会自动发送.complete
的event
)。BehaviorRelay
有一个value
属性,咱们经过这个属性能够获取最新值。而经过它的accept()
方法能够对值进行修改。
Variable
样例咱们能够改用成 BehaviorRelay
,代码以下:import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let disposeBag = DisposeBag() //建立一个初始值为111的BehaviorRelay let subject = BehaviorRelay<String>(value: "111") //修改value值 subject.accept("222") //第1次订阅 subject.asObservable().subscribe { print("第1次订阅:", $0) }.disposed(by: disposeBag) //修改value值 subject.accept("333") //第2次订阅 subject.asObservable().subscribe { print("第2次订阅:", $0) }.disposed(by: disposeBag) //修改value值 subject.accept("444") } } 复制代码
运行结果:
accept()
方法与 value
属性配合来实现。(这个经常使用在表格上拉加载功能上,BehaviorRelay
用来保存全部加载到的数据)import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let disposeBag = DisposeBag() //建立一个初始值为包含一个元素的数组的BehaviorRelay let subject = BehaviorRelay<[String]>(value: ["1"]) //修改value值 subject.accept(subject.value + ["2", "3"]) //第1次订阅 subject.asObservable().subscribe { print("第1次订阅:", $0) }.disposed(by: disposeBag) //修改value值 subject.accept(subject.value + ["4", "5"]) //第2次订阅 subject.asObservable().subscribe { print("第2次订阅:", $0) }.disposed(by: disposeBag) //修改value值 subject.accept(subject.value + ["6", "7"]) } } 复制代码
运行结果:
buffer 方法做用是缓冲组合,第一个参数是缓冲时间,第二个参数是缓冲个数,第三个参数是线程。 该方法简单来讲就是缓存
Observable
中发出的新元素,当元素达到某个数量,或者通过了特定的时间,它就会将这个元素集合发送出来。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { let subject = PublishSubject<String>() //每缓存3个元素则组合起来一块儿发出。 //若是1秒钟内不够3个也会发出(有几个发几个,一个都没有发空数组 []) subject .buffer(timeSpan: 1, count: 3, scheduler: MainScheduler.instance) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject.onNext("a") subject.onNext("b") subject.onNext("c") subject.onNext("1") subject.onNext("2") subject.onNext("3") } } 复制代码
运行结果:
window 操做符和 buffer 十分类似。不过 buffer 是周期性的将缓存的元素集合发送出来,而 window 周期性的将元素集合以 Observable 的形态发送出来。 同时 buffer 要等到元素搜集完毕后,才会发出元素序列。而 window 能够实时发出元素序列。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { let subject = PublishSubject<String>() //每3个元素做为一个子Observable发出。 subject .window(timeSpan: 1, count: 3, scheduler: MainScheduler.instance) .subscribe(onNext: { [weak self] in print("subscribe: \($0)") $0.asObservable() .subscribe(onNext: { print($0) }) .disposed(by: self!.disposeBag) }) .disposed(by: disposeBag) subject.onNext("a") subject.onNext("b") subject.onNext("c") subject.onNext("1") subject.onNext("2") subject.onNext("3") } } 复制代码
运行结果:
let disposeBag = DisposeBag() Observable.of(1, 2, 3) .map { $0 * 10} .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 10 20 30
map
在作转换的时候容易出现“升维”的状况。即转变以后,从一个序列变成了一个序列的序列。- 而
flatMap
操做符会对源Observable
的每个元素应用一个转换方法,将他们转换成Observables
。 而后将这些Observables
的元素合并以后再发送出来。即又将其 "拍扁"(降维)成一个Observable
序列。- 这个操做符是很是有用的。好比当
Observable
的元素本生拥有其余的Observable
时,咱们能够将全部子Observables
的元素发送出来。
let disposeBag = DisposeBag() let subject1 = BehaviorSubject(value: "A") let subject2 = BehaviorSubject(value: "1") let variable = Variable(subject1) variable.asObservable() .flatMap { $0 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("B") variable.value = subject2 subject2.onNext("2") subject1.onNext("C") 复制代码
运行结果:
let disposeBag = DisposeBag() let subject1 = BehaviorSubject(value: "A") let subject2 = BehaviorSubject(value: "1") let variable = Variable(subject1) variable.asObservable() .flatMapLatest { $0 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("B") variable.value = subject2 subject2.onNext("2") subject1.onNext("C") 复制代码
运行结果:
- flatMapFirst 与 flatMapLatest 正好相反:flatMapFirst 只会接收最初的 value 事件。
- 该操做符能够防止重复请求: 好比点击一个按钮发送一个请求,当该请求完成前,该按钮点击都不该该继续发送请求。即可该使用 flatMapFirst 操做符。
let disposeBag = DisposeBag() let subject1 = BehaviorSubject(value: "A") let subject2 = BehaviorSubject(value: "1") let variable = Variable(subject1) variable.asObservable() .flatMapFirst { $0 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("B") variable.value = subject2 subject2.onNext("2") subject1.onNext("C") 复制代码
运行结果:
concatMap 与 flatMap 的惟一区别是:当前一个 Observable 元素发送完毕后,后一个Observable 才能够开始发出元素。或者说等待前一个 Observable 产生完成事件后,才对后一个 Observable 进行订阅。
let disposeBag = DisposeBag() let subject1 = BehaviorSubject(value: "A") let subject2 = BehaviorSubject(value: "1") let variable = Variable(subject1) variable.asObservable() .concatMap { $0 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("B") variable.value = subject2 subject2.onNext("2") subject1.onNext("C") subject1.onCompleted() //只有前一个序列结束后,才能接收下一个序列 复制代码
运行结果:
scan 就是先给一个初始化的数,而后不断的拿前一个结果和最新的值进行处理操做。
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4, 5) .scan(0) { acum, elem in acum + elem } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果
groupBy 操做符将源 Observable 分解为多个子 Observable,而后将这些子 Observable 发送出来。 也就是说该操做符会将元素经过某个键进行分组,而后将分组后的元素序列以 Observable 的形态发送出来。
let disposeBag = DisposeBag() //将奇数偶数分红两组 Observable<Int>.of(0, 1, 2, 3, 4, 5) .groupBy(keySelector: { (element) -> String in return element % 2 == 0 ? "偶数" : "基数" }) .subscribe { (event) in switch event { case .next(let group): group.asObservable().subscribe({ (event) in print("key:\(group.key) event:\(event)") }) .disposed(by: disposeBag) default: print("") } } .disposed(by: disposeBag) 复制代码
运行结果:
该操做符就是用来过滤掉某些不符合要求的事件。
let disposeBag = DisposeBag() Observable.of(2, 30, 22, 5, 60, 3, 40 ,9) .filter { $0 > 10 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 1, 1, 4) .distinctUntilChanged() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果:
限制只发送一次事件,或者知足条件的第一个事件。 若是存在有多个事件或者没有事件都会发出一个 error 事件。 若是只有一个事件,则不会发出 error 事件。
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .single{ $0 == 2 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) Observable.of("A", "B", "C", "D") .single() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果:
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .elementAt(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 3
该操做符能够忽略掉全部的元素,只发出 error 或 completed 事件。 若是咱们并不关心 Observable 的任何元素,只想知道 Observable 在何时终止,那就可使用 ignoreElements 操做符。
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .ignoreElements() .subscribe{ print($0) } .disposed(by: disposeBag) 复制代码
运行结果: completed
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .take(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 1 2
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .takeLast(1) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 4
let disposeBag = DisposeBag() Observable.of(1, 2, 3, 4) .skip(2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 3 4
- Sample 除了订阅源 Observable 外,还能够监视另一个 Observable, 即 notifier 。
- 每当收到 notifier 事件,就会从源序列取一个最新的事件并发送。而若是两次 notifier 事件之间没有源序列的事件,则不发送值。
let disposeBag = DisposeBag() let source = PublishSubject<Int>() let notifier = PublishSubject<String>() source .sample(notifier) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) source.onNext(1) //让源序列接收接收消息 notifier.onNext("A") source.onNext(2) //让源序列接收接收消息 notifier.onNext("B") notifier.onNext("C") source.onNext(3) source.onNext(4) //让源序列接收接收消息 notifier.onNext("D") source.onNext(5) //让源序列接收接收消息 notifier.onCompleted() 复制代码
运行结果:
1 2 4 5
- debounce 操做符能够用来过滤掉高频产生的元素,它只会发出这种元素:该元素产生后,一段时间内没有新元素产生。
- 换句话说就是,队列中的元素若是和下一个元素的间隔小于了指定的时间间隔,那么这个元素将被过滤掉。
- debounce 经常使用在用户输入的时候,不须要每一个字母敲进去都发送一个事件,而是稍等一下取最后一个事件。
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { //定义好每一个事件里的值以及发送的时间 let times = [ [ "value": 1, "time": 0.1 ], [ "value": 2, "time": 1.1 ], [ "value": 3, "time": 1.2 ], [ "value": 4, "time": 1.2 ], [ "value": 5, "time": 1.4 ], [ "value": 6, "time": 2.1 ] ] //生成对应的 Observable 序列并订阅 Observable.from(times) .flatMap { item in return Observable.of(Int(item["value"]!)) .delaySubscription(Double(item["time"]!), scheduler: MainScheduler.instance) } .debounce(0.5, scheduler: MainScheduler.instance) //只发出与下一个间隔超过0.5秒的元素 .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) } } 复制代码
运行结果: 1 5 6
当传入多个 Observables
到 amb
操做符时,它将取第一个发出元素或产生事件的 Observable
,而后只发出它的元素。并忽略掉其余的 Observables
。
实例 2.9.1
let disposeBag = DisposeBag() let subject1 = PublishSubject<Int>() let subject2 = PublishSubject<Int>() let subject3 = PublishSubject<Int>() subject1 .amb(subject2) .amb(subject3) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject2.onNext(1) subject1.onNext(20) subject2.onNext(2) subject1.onNext(40) subject3.onNext(0) subject2.onNext(3) subject1.onNext(60) subject3.onNext(0) subject3.onNext(0) 复制代码
运行结果: 1 2 3
该方法依次判断 Observable 序列的每个值是否知足给定的条件。 当第一个不知足条件的值出现时,它便自动完成。
实例 2.9.2
let disposeBag = DisposeBag() Observable.of(2, 3, 4, 5, 6) .takeWhile { $0 < 4 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果: 2 3
- 除了订阅源
Observable
外,经过takeUntil
方法咱们还能够监视另一个Observable
, 即notifier
。- 若是
notifier
发出值或complete
通知,那么源Observable
便自动完成,中止发送事件。![]()
let disposeBag = DisposeBag() let source = PublishSubject<String>() let notifier = PublishSubject<String>() source .takeUntil(notifier) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) source.onNext("a") source.onNext("b") source.onNext("c") source.onNext("d") //中止接收消息 notifier.onNext("z") source.onNext("e") source.onNext("f") source.onNext("g") 复制代码
输出结果: a b c d
- 该方法用于跳过前面全部知足条件的事件。
- 一旦遇到不知足条件的事件,以后就不会再跳过了。
![]()
let disposeBag = DisposeBag() Observable.of(2, 3, 4, 5, 6) .skipWhile { $0 < 4 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) } } 复制代码
运行结果: 4 5 6
- 同上面的
takeUntil
同样,skipUntil
除了订阅源Observable
外,经过skipUntil
方法咱们还能够监视另一个Observable
, 即notifier
。- 与
takeUntil
相反的是。源Observable
序列事件默认会一直跳过,直到notifier
发出值或complete
通知。
let disposeBag = DisposeBag() let source = PublishSubject<Int>() let notifier = PublishSubject<Int>() source .skipUntil(notifier) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) source.onNext(1) source.onNext(2) source.onNext(3) source.onNext(4) source.onNext(5) //开始接收消息 notifier.onNext(0) source.onNext(6) source.onNext(7) source.onNext(8) //仍然接收消息 notifier.onNext(0) source.onNext(9) 复制代码
运行结果: 6 7 8 9
该方法会在 Observable 序列开始以前插入一些事件元素。即发出事件消息以前,会先发出这些预先插入的事件消息
let disposeBag = DisposeBag() Observable.of("2", "3") .startWith("1") .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果:
1 2 3
let disposeBag = DisposeBag() Observable.of("2", "3") .startWith("a") .startWith("b") .startWith("c") .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) 复制代码
运行结果:
c b a 2 3
该方法能够将多个(两个或两个以上的)
Observable
序列合并成一个Observable
序列。
let disposeBag = DisposeBag() let subject1 = PublishSubject<Int>() let subject2 = PublishSubject<Int>() Observable.of(subject1, subject2) .merge() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext(20) subject1.onNext(40) subject1.onNext(60) subject2.onNext(1) subject1.onNext(80) subject1.onNext(100) subject2.onNext(1) 复制代码
运行结果:
20 40 60 1 80 100 1
该方法能够将多个(两个或两个以上的)
Observable
序列压缩成一个Observable
序列。 并且它会等到每一个Observable
事件一一对应地凑齐以后再合并。
let disposeBag = DisposeBag() let subject1 = PublishSubject<Int>() let subject2 = PublishSubject<String>() Observable.zip(subject1, subject2) { "\($0)\($1)" } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext(1) subject2.onNext("A") subject1.onNext(2) subject2.onNext("B") subject2.onNext("C") subject2.onNext("D") subject1.onNext(3) subject1.onNext(4) subject1.onNext(5) 复制代码
运行结果:
1A 2B 3C 4D
好比咱们想同时发送两个请求,只有当两个请求都成功后,再将二者的结果整合起来继续往下处理。这个功能就能够经过 zip 来实现。
//第一个请求 let userRequest: Observable<User> = API.getUser("me") //第二个请求 let friendsRequest: Observable<Friends> = API.getFriends("me") //将两个请求合并处理 Observable.zip(userRequest, friendsRequest) { user, friends in //将两个信号合并成一个信号,并压缩成一个元组返回(两个信号均成功) return (user, friends) } .observeOn(MainScheduler.instance) //加这个是应为请求在后台线程,下面的绑定在前台线程。 .subscribe(onNext: { (user, friends) in //将数据绑定到界面上 //....... }) .disposed(by: disposeBag) 复制代码
- 该方法一样是将多个(两个或两个以上的)
Observable
序列元素进行合并。- 但与
zip
不一样的是,每当任意一个Observable
有新的事件发出时,它会将每一个Observable
序列的最新的一个事件元素进行合并。
let disposeBag = DisposeBag() let subject1 = PublishSubject<Int>() let subject2 = PublishSubject<String>() Observable.combineLatest(subject1, subject2) { "\($0)\($1)" } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext(1) subject2.onNext("A") subject1.onNext(2) subject2.onNext("B") subject2.onNext("C") subject2.onNext("D") subject1.onNext(3) subject1.onNext(4) subject1.onNext(5) 复制代码
运行结果:
该方法将两个
Observable
序列合并为一个。每当self
队列发射一个元素时,便从第二个序列中取出最新的一个值。
图解:
实例 2.10.1
let disposeBag = DisposeBag() let subject1 = PublishSubject<String>() let subject2 = PublishSubject<String>() subject1.withLatestFrom(subject2) .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("A") subject2.onNext("1") subject1.onNext("B") subject1.onNext("C") subject2.onNext("2") subject1.onNext("D") 复制代码
运行结果:
1 1 2
switchLatest
有点像其余语言的switch
方法,能够对事件流进行转换。- 好比原本监听的
subject1
,我能够经过更改variable
里面的value
更换事件源。变成监听subject2
。
let disposeBag = DisposeBag() let subject1 = BehaviorSubject(value: "A") let subject2 = BehaviorSubject(value: "1") let variable = Variable(subject1) variable.asObservable() .switchLatest() .subscribe(onNext: { print($0) }) .disposed(by: disposeBag) subject1.onNext("B") subject1.onNext("C") //改变事件源 variable.value = subject2 subject1.onNext("D") subject2.onNext("2") //改变事件源 variable.value = subject1 subject2.onNext("3") subject1.onNext("E") 复制代码
运行结果: