RxSwift基础之一 - Observables & Subjects

本文章内部分图片资源来自RayWenderlich.com

本文结合自己的理解来总结介绍一下RxSwift最基本的一些概念,当然只这一篇是肯定介绍不完RxSwift的,这篇文算是起个头,以后有时间时候则会继续

简要概述

"Rx"是ReactiveX的缩写,它是目前比较流行比较火的一种开发方式的库,"Rx"本身可以说是一种跨平台的标准,不管是web还是移动开发,都能用"Rx"的思维和方法来完成你的工作。
"Rx"有自己的社区论坛。除了RxSwift之外,还有类似RxJava, RxJS, RxKotlin, Rx.NET........都是基于不同的开发语言的Rx的库,他们之间其实都是相通的,都有相同的API,除了用的语言不同,所以说如果以后你使用别的语言做别的方面的开发,同样是可以用到相同的思维甚至相同的方法接口(除了语言不同)来编程。

这是Rx大家庭的主页地址:reactivex
在里面可以看到Rx大家庭的所有成员如下

Rx Family

Rx自己的定义是这样写的:

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

Rx就是基于异步Event(事件)序列的响应式编程,它可以简化异步编程方法,提供更优雅的数据绑定,让你可以时刻响应新的数据同时顺序地处理它们。
另外如果有使用MVVM开发模式的小伙伴通过RxSwift就可以获得更加方便的数据绑定的方法,可以使得你的MVVM如虎添翼。

这是RxSwift在github上的地址:RXSwift
可以在这个地址页面下方看到,如果你使用CocoaPods来引入RXSwift到你项目工程里时候,它让你要引入两个库,如下

pod 'RxSwift',    '~> 3.0'
pod 'RxCocoa',    '~> 3.0'

那么这个RxCocoa又是什么呢?上面说到RxSwift它并纯不是针对iOS开发,而只是基于Swift语言的Rx标准实现接口库,所以RxSwift里不包含任何Cocoa或者UI方面的类。
而RxCocoa就是基于RxSwift针对于iOS开发的一个库,它通过Extension的方法给原生的比如UI控件添加了Rx的特性,使得你更容易订阅和响应这些控件的事件。
或者再极端一点说就是:使用Rx可以对你的程序里的事件传递响应方法做到一统江湖,要将以前你常用的那些事件传递方法,比如delegate、notification、target-action等等全部替换成Rx的"信号链"方式。

不过这篇文章里不会去写RxCocoa的使用方法,而是主要介绍RxSwift中最基本的核心概念,只有从最根本的理解了才能使用的得心应手。


Rx的根基Observables

Observable<T>这个类就是Rx框架的基础,它的作用就是可以异步地产生一系列的Event(事件),同时这些Event还可以携带数据,它的泛型<T>就是用来指定这个Event携带的数据的类型。
即一个Observable<T>对象会随着时间推移不定期地发出event(element : T)这样一个东西。
不过首先需要有一个Observer订阅者来订阅Observable<T>,这样这个订阅者才能收到Observable<T>不时发出的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
}

可以看到Event的定义就是一个枚举,也就是一个Observable是可以发出3种不同类型的Event事件:
next:next事件就是那个可以携带数据<T>的事件,可以说它就是一个“最正常”的事件。
error:error事件表示一个错误,它可以携带具体的错误内容,一旦Observable发出了error event,则这个Observable就等于终止了,以后它再也不会发出event事件了。
completed:completed事件表示Observable发出的事件正常地结束了,跟error一样,一旦Observable发出了completed event,则这个Observable就等于终止了,以后它再也不会发出event事件了。

Observable<Int>

从上图可以看出Observable就相当于一个sequence(序列),它是基于时间的,图片中的三个圈就相当于在三个不同的时间点上发出的Event事件。因为这个Observable 的泛型是Int:Observable<Int>,则表示它发出的Event携带的数据的数据类型是Int,就好像图上的一样,三个Event携带的数据分别是数字1、2和3。

Observable end by Completed Event
Observable end by Error Event

上面两个图分别表示了两个Observable分别以Completed Event和Error Event为终结,一旦Observable终结了,则它以后再也不会发出新的Event 了。


创建Observable

我们来写实际写一些代码,来创建Observable和订阅它。你可以不用创建一个App项目工程,创建一个Xcode提供的playground项目即可。另外不管你是创建app还是playground,都请先导入RxSwift这个库!!!

我们来创建几个Observable,Observable提供了just,of,from方法来通过默认值创建一个Observable,如下

// 1
let observable: Observable<Int> = Observable<Int>.just(1)
// 2
let observable2 = Observable.of("One", "Two", "Three")
// 3
let observable3 = Observable.from([1, 2, 3])

我们分别通过三个方法来创建了三个observable对象
1.第一个通过just()方法,这个方法通过传入一个默认值来初始化。可以看到我们显式地标注出了observable的类型为Observable<Int>,指定了这个Observable所发出的事件携带的数据类型必须是Int类型的。

2.第二个通过of()方法创建,这个方法可以接受可变数量的参数,不过注意他们一定要是同类型的,可以看到我并没有显式地声明出Observable2的泛型类型,因为Swift会自动推断类型,它看到我传入的element全是String类型,便默认地认为Observable2的类型为Observable<String>。你可以按住Alt键同时鼠标点击observable2,看看它是不是真的是Observable<String>


observable2类型

3.第三种方法通过from()创建,它需要一个数组参数,而数据里的元素就会被当做这个observable3所发出event携带的数据内容,也就是用of()方法创建和把of()方法所有元素放在一个数组里再传入from()方法创建的效果是一样的。你也可以按住Alt看看observable3的类型会被推断为Observable<Int>而不是Observable<Array>


observable3类型

订阅Observable

光有了Observable还不行,还需要有人来订阅它,它才能发出Event,不然没有理它它的Event发给谁听呢?
在上面创建Obeservale的代码下面加入如下代码:

func subscribeOne() {
  print("------------ subscribe one ------------")
  observable.subscribe { event in
    print(event)
  }
  
  print("------------ subscribe two ------------")
  observable2.subscribe { event in
    print(event)
  }
  
  print("------------ subscribe three ------------")
  observable3.subscribe { event in
    print(event)
  }
}

记得要调用一下这个测试方法subscribeOne() 。
很简单,我们通过subscribe()订阅了之前创建的3个Observable对象,这个block的回调参数就是被发出的event事件,我们直接print打印出来看看结果是什么:


subscribeOne() print result

可以看到我们在初始化Observable对象时候设置的默认值都被顺序地通过.next事件发送了出来,当Observable对象的初始数据都发送完了,它还会自动地发送一个.completed事件出来。

但是注意此时打印出来的是event是Event类型,比如上面看到的next(1),next(One)等等,我们如果获取到这个事件里的数据呢?很简单,通过event.element即可获取到。但是这样不是有点麻烦么,每次都要去event里读取这个属性,所以RxSwift还提供了另一个subscribe方法如下:

func subscribeTwo() {
  observable3.subscribe(onNext: { element in
    print(element)
  }, onError: { error in
    print(error)
  }, onCompleted: { 
    print("completed")
  }, onDisposed: {
    
  })
}

先将上面调用subscribeOne() 代码给注释掉,再调用一次subscribeTwo()看看结果


subscribeTwo() print result

可以看到这个方法其实就是将event给分类了,通过不同的block进行回调,.next就通过onNext回调,.error的就通过onError回调,同时它会把event携带的数据直接解包出来作为参数直接回调回来,所以我们print(element)打印出来的结果就是.next事件所携带的数据了。
有人可能注意到最后有一个onDisposed的block,这个稍后会讲到,暂时可以不用管。

PS: subscribe()方法的onNext、onError、onCompleted和onDisposed这四个回调block参数都是有默认值的,即它们都是可选的,你可以只设置onNext而不管其他的情况,比如:observable.subscribe( onNext: { print($0) } )。


垃圾回收Dispose

一个Observable被创建出来后它不会马上就开始被激活从而发出Event,而是要等到它被某个人订阅了才会激活它,激活之后一直等到Observable发出了.error或者.completed的event后,它才被终结。同时你也可以手动的取消一个订阅的行为,当你觉得这个订阅结束了不再需要了就需要调用对应的方法把这个订阅给销毁掉,有点类似以前的内存管理,否则可能会引起内存泄漏。
当你对一个Observable调用订阅方法的时候,这个方法其实是有返回值的,返回值的类型是Disposable,从它的类型名字意思就可以看出,这个类型是可丢弃可销毁的。如果你进入RxSwift的代码里看一看:

/// Respresents a disposable resource.
public protocol Disposable {
    /// Dispose resource.
    func dispose()
}

Disposable其实就是一个protocol协议,而这个协议里只有一个方法就是dispose(),所以要取消一个订阅行为这就要用到Disposable协议里的dispose()方法即可。
方法如下:

func disposeOne() {
  
  // 1
  let subscription = observable.subscribe { event in
    print(event)
  }
  // 2
  subscription.dispose()
  
}

1.订阅我们最开始创建过的observable对象,并且使用一个叫subscription的常量来接收订阅方法的返回值
2.对这个订阅行为调用dispose()方法
注意,如果一个订阅行为被dispose了,那么之后observable如果再发出event,这个已经dispose的订阅就收不到消息了(虽然当前例子中的observable不会再发出event了)

除了世界使用dispose()方法之外,还有一个平时更常用dispose订阅的模式,是使用一个叫DisposeBag的对象来管理一堆订阅行为的销毁,代码如下

func disposeTwo() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  observable2.subscribe { event in
    print(event)
  }.disposed(by: disposeBag)
  
  // 3
 observable3.subscribe { event in
    print(event)
  }.disposed(by: disposeBag)
  
}

1.创建一个DisposeBag对象,可以把它看成一个垃圾袋,把用过的订阅行为都丢进去,然后这个disposeBag就会在自己快要dealloc的时候对它里面的所有订阅行为都调用dispose()方法。
2.订阅我们最开始创建的observable2,紧接着就把它丢进我们的“垃圾袋”里。
3.同理2,对最开始创建的observable3做同样的操作。

好的说完了Dispose,我们现在再想想前面那个subcribe()方法里,有一个我们还没有解释的onDisposed的block的作用是什么。不过我感觉现在也不用怎么解释你也应该知道了,它就是在这个订阅行为被dispose之后会回调的block啦!


其他创建方法

Observable初始化除了上面提到的just,of,from之外,还有可以通过empty()创建一个空内容的Observable,也能通过never()方法创建一个永远不会发出Event的Observable,还能通过range()方法指定起始和结束数值,来通过值的范围创建一个用这个范围内所有值作为初始值的Observable。

另外有两个比较值得一说的就是Observable创建方法,一个就叫create(),一个叫deferred():

create()方法

Observable 可通过create()方法创建,create方法接受一个block形式的参数,这个block任务就是处理一个订阅来了的处理情况。直接看代码更简单:

func create() {
  // 1
  let ob = Observable<String>.create{observer in
    // 2
    observer.onNext("a")
    // 3
    observer.onCompleted()
    // 4
    return Disposables.create()
  }
  
  // 5
  ob.subscribe {
    print($0)
  }
}

1.通过create创建一个泛型为String的Observable对象,注意看他的block参数,这个block有一个回调参数observer就是订阅这个Observable对象的订阅者,可以理解为当一个订阅者订阅这个Observable对象的时候,就会将订阅者作为参数传入这个block来执行一些内容。
2.我们对订阅者发出了.next事件,且携带了一个数据a
3.对订阅者发出了.completed事件
4.最后因为一个订阅行为会有一个Disposable类型的返回值,所以在结尾一定要returen一个Disposable
5.订阅测试

看一看这个测试方法运行结果:


create() print output


deferred()方法

deferred()创建方法,使用这个方法创建Observable对象相当于是一个Observable工厂,deferred()方法需要传入一个block来执行延迟创建的行为,这个block里就是真正的实例化对象的地方。为了更容易理解就直接上代码:

func deferredTest() {
  
  // 1
  var chinese = false
  
  // 2
  let factory : Observable<String> = Observable.deferred {
    
    // 3
    chinese = !chinese
    
    // 4
    if chinese {
      return Observable.of("一", "二", "三")
    }else {
      return Observable.of("One", "Two", "Three")
    }
    
  }

  // 5
  let disposeBag = DisposeBag()
  
  // 6
  factory.subscribe { event in
    print("1:", event)
  }.disposed(by: disposeBag)
  // 7
  factory.subscribe { event in
    print("2:", event)
  }.disposed(by: disposeBag)
}

1.我们创建了一个Bool类型的变量来表示是否为中文
2.我们创建了一个Observable工厂,虽然它对外看起来更其他Observable没什么不同,就是一个Observable<String>类型。但是我们通过deferred()方法将它的初始化给延迟了,通过传入的block来做Observable实例的初始化并且返回。
3.我们先翻转chinese,让每次执行这个block时候都不同
4.我们根据chinese标示来选择性的创建是包含中文还是英文的1、2、3值的Observable实例对象并且返回。

至此我们创建了一个Observable工厂,也就是说你每次调用factory获取到的是不同的Observable对象,接下来的代码就是用来验证的。

5.创建了一个DisposeBag
6.订阅factory,并且打印出来event,同时通过打印一个"1"表示这是第一个订阅的结果
7.同理再次订阅factory,打印结果通过2来区分。

现在来调用一次我们新写的这个deferredTest()方法,看看输出结果是什么:

deferredTest() print output

可以看到我们虽然两次都是订阅了factory,但其实订阅的真是Observable对象是不同的,一个发出Event的值是中文的,一个发出的Event的值是英文的。






Subjects

接下来进入这篇的第二个主题就是Subjects。
你可能会发现上面的说的Observable有个问题就是:我们在创建一个Observable的时候就要预先将它以后要发出的Event所携带的数据都准备好,等到有人订阅它再将数据通过Event发出去。
而实际情况通常是需要Observable在运行时动态地“收到”或者说“产生”出一个新的数据,再通过Event发送出去。也就是这些数据不是事先定好的,而已在程序运行的时候才能知道的。比如说如果我们订阅着一个输入框的输入内容,我们不可能事先就知道用户会输入什么,而是用户每输入一个字,这个输入框关联的Observable就会发出一个Event携带着这个用户输入的内容,通知给所有订阅者。

所以我们真正需要的应该是一个类似即是可以发出Event的Observable,同样也是可以接受新数据的订阅者。这个就是我们要说的Subjects,Subjects能够动态地接收新的值,这就相当于它本身一定程度上是一个订阅者。当它有了新的值之后就会通过Event将新值发出给他的所有订阅者,这样看来它又是一个Observable了。

Subjects的分类

一共有四种Subjects,分别叫做PublishSubject,BehaviorSubject,ReplaySubject和Variable。他们之间其实大致相同,但是有具体自己鲜明的特点,可以针对不同的使用场景进行选择。
可以说他们都是Observable,他们的订阅者都能收到他们发出的新的Event,他们之间最大的区别只是在于一个新的订阅者刚订阅它的时刻能不能收到Subject以前发出过的旧Event,如果能的话那么收到多少个。

下面来一一地介绍


PublishSubjects

PublishSubject可以说是最普通的Subject,它不需要初始值就能创建。PublishSubject的订阅者从他们开始订阅的时间点起,可以收到以后Subject发出的新Event,而不会收到他们订阅前已发出的Event。
直到Subject发出.complete或者.error的Event后,Subject则终结了,它也就不会再发出.next事件。但是对于在subject终结后再订阅他的订阅者,也能收到subject发出的一条.complete或者.error的event,告诉这个新的订阅者它已经终结了。
看一看下面这个时序图就跟直观的理解了,最上面这条是一个PublishSubject,他在三个事件点上发出了三个Event,第二条和第三条线是两个新的订阅,虚线向上的箭头表示他们订阅PublishSubject的动作,可以很清楚的看看PublishSubject的订阅者只能收到他们订阅后的Event。


PublishSubjects

接下来上代码:

func publishSubject() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = PublishSubject<String>()
  
  // 3
  subject.onNext("Hello_1")
  
  // 4
  subject.subscribe(onNext: { string in
    print("subscribe_1:", string)
  }, onCompleted:{
    print("subscribe_1: onCompleted")
  }).disposed(by: disposeBag)
  
  // 5
  subject.onNext("Hello_2")
  
  // 6
  subject.subscribe(onNext: { string in
    print("subscribe_2:", string)
  }, onCompleted:{
    print("subscribe_2: onCompleted")
  }).disposed(by: disposeBag)
  
  // 7
  subject.onNext("Hello_3")
  
  // 8
  subject.onCompleted()
  
  // 9
  subject.onNext("Hello_4")

  // 10
  subject.subscribe(onNext: { string in
    print("subscribe_3:", string)
  }, onCompleted:{
    print("subscribe_3: onCompleted")
  }).disposed(by: disposeBag)
}
publishSubject() print output

运行一下我们的测试方法 publishSubject() ,结合输出结果来一步一步讲解:
1.创建了一个DisposeBag
2.创建一个PublishSubject
3.对subject调用onNext方法,这个方法相当于subject接受到一个.next事件,同时它会马上把这个事件转发给他当前所有的订阅者(所以说Subject即使Observable又是订阅者)。但是此时并没有任何订阅subject的行为,所以你能看到上面并没有打印出"Hello_1"这个信息。
4.我们订阅subject,打印出.next的事件数据和.completed事件
5.再次调用subject的onNext方法,再次发出一句"Hello_2",可以看到打印"subscribe_1: Hello_2"这一句,可见我们刚刚的订阅有作用了
6.我们再次订阅subject,输出用"subscribe_2"来区分标示
7.再次调用subject的onNext方法,再次发出一句"Hello_3"。能看到两次订阅都打印出了"Hello_3"
8.对subject调用onCompleted方法,表示这个subject完成了,它以后再也不会发出.next事件了。可以看到subscribe_1和subscribe_2都打印出了.competed事件。
9.再次调用subject的onNext方法,再次发出一句"Hello_4",但是你看没有任何"Hello_4"的打印,因为subject已经completed了,他已无法再发出.next事件了
10.我们再次订阅subject,可以看到立刻打印了"subscribe_3: onCompleted",这表示虽然第三次订阅是发生在subject发出.completed之后,但是第三次订阅也能收到subject的.completed事件,告诉你我已经结束了,别再瞎订阅了!!!


BehaviorSubjects

BehaviorSubject需要通过一个默认初始值来创建,当一个订阅者来订阅它的时候,这个订阅者会立即收到BehaviorSubjects的上一个发出的event,之后就跟正常的情况一样,它也会接收到BehaviorSubjects之后发出的新的event。所以它必须有初始值,不然当它刚创建就有人来订阅它的时候,它就没有"上一个值"能用来发出了。

BehaviorSubjects

再上代码和输出结果一起来讲解:

func behaviorSubject() {
  
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = BehaviorSubject(value: "A")

  // 3
  subject.subscribe { event in
      print("subscribe_1:", event)
  }.disposed(by: disposeBag)

  // 4
  subject.onNext("B")

  // 5
  subject.onError(NSError(domain: "xxx", code: 0, userInfo: nil))
  
  // 6
  subject.subscribe { event in
    print("subscribe_2:", event)
  }.disposed(by: disposeBag)

}
behaviorSubject() print output

记得调用一次测试方法behaviorSubject()。
1.又是先创建一个DisposeBag
2.创建一个初始值为"A"的BehaviorSubject,初始值你也可以理解为这个Subject一创建就调用了一个onNext方法,发出了一个值为"A"的Event
3.订阅一次Subject,可以看到打印出了这句"subscribe_1: next(A)",虽然这个订阅1晚于Subject"发出"A事件。但是他也能收到Subject上一个发出的值(这个例子里是它的创建默认值)。
4.调用subject的onNext方法再次发出一个含有"B"的.next事件,可以看到subscribe_1也正常的打印出了
5.调用subject的onError方法,表示subject遇到一个错误,并且终结了,以后也不会发出.next事件了。可以看到subscribe_1也正常打印出了这个错误事件
6.再次订阅这个已经终结的subject,可以看到subscribe_2页打印出了Subject的错误,但未打印出任何.next事件值了。


ReplaySubject

ReplaySubject的关键就在于它在创建时候要给它设置一个bufferSize,这个bufferSize就表示它对于它发出过的event进行缓存的一个数量大小。

比如一个ReplaySubjects的bufferSize设置为2,它发出了3个.next的event,那么它会将后两个(比较新的)发出的event给缓存起来,此时如果有一个subscriber订阅了这个ReplaySubject,那么这个subscriber就会立即收到前面缓存的两个.next的event。
(所以上面说过的BehaviorSubject有点类似于bufferSize为1的ReplaySubject)

如果在订阅之前,ReplaySubject就发出了.error或者.complete的event,那么此时如果一个subscriber订阅已经结束的ReplaySubject,除了会收到缓存的.next的event还会收到那个终结的.error或者.complete的event。
(这点是跟BehaviorSubject不同的,BehaviorSubject在终结后如果再订阅只能收到终结的.error或者.complete,而不能收到上一个发出的.next了)

所以这个bufferSize的大小很关键,如果设置不当很容易对内存消耗产生很大的压力。


ReplaySubjects

上代码示例:


func replaySubject() {
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  let subject = ReplaySubject<String>.create(bufferSize: 2)
  
  // 3
  subject.onNext("A")
  subject.onNext("B")
  subject.onNext("C")
  
  // 4
  subject.subscribe { event in
      print("subscribe_1:", event)
  }.disposed(by: disposeBag)
  
  // 5
  subject.onNext("D")
  
  // 6
  subject.subscribe { event in
    print("subscribe_2:", event)
  }.disposed(by: disposeBag)
  
  // 7
  subject.onCompleted()
  
  // 8
  subject.subscribe { event in
    print("subscribe_3:", event)
  }.disposed(by: disposeBag)
}
replaySubject() print output

还是记得调用一次上面刚写的测试方法replaySubject()。
1.照旧创建一个DisposeBag
2.创建一个ReplaySubject,bufferSize设置为2,表示他会缓存2个发出的.next事件
3.我们调用3次subject的onNext方法,发出"A"、"B"、"C"三个值,因为我们设置了bufferSize为2,以后最后两个event会被缓存下来
4.开始第一次的订阅,看打印是不是输出了"subscribe_1: next(B)"和"subscribe_1: next(C)"。这就表示这次订阅刚发生的时候就会收到subject缓存的已发出过的event
5.再次调用subject的onNext方法发出一个"D",看打印subscribe_1也正常的接收到了这个时间。注意这个时候缓存的两个值就从B和C变为了C和D
6.第二次订阅subject看输出确实是"subscribe_2: next(C)"和"subscribe_2: next(D)",这就验证了subject的缓存内容确实在不断更新
7.调用subject的onCompleted方法,终结了这个subject。可以看到subscribe_1和subscribe_2都收到了这个完成的事件并且打印了出来
8.第三次订阅,因为这个subject已经终结了,所以可以看到subscribe_3除了接收并输出了缓存的两个事件以外还接收到了completed事件。这里需要注意的就是,跟前面的subject不同,即使subject终结了,后来的订阅者也还是能收到已缓存的.next事件。


Variables

最后一类叫Variables,它其实是封装了一个BehaviorSubjects,它除了具备BehaviorSubjects的功能,能够向它的订阅者发出上一个的event和之后新创建的event之外,同时还把会把当前发出的值保存为自己的状态,正因为它是对BehaviorSubjects的封装,所以它也必须要通过一个默认的初始值进行创建。
通俗点将就是Variables有一个value属性,你通过改变这个value属性的值就相当于对一般的Subjects调用onNext()方法,而这个最新的onNext()的值就被一只保存在value属性里了,直到你再次修改它。
但是Variables的value不能发出.error和.complete的event,即你不能给value赋值.error或者.complete,当Variables要deallocate的时候他会自动的complete。
Variables本身没有subscribe()方法,但是所有Subjects都有一个asObservable()方法,这个方法就会返回这个Subject的Observable类型,拿到这个Observable类型我们就能订阅它了。
Variables之所以叫做Variables,就是在于它可以适用于多变的使用场景。比如你可以把它当做一般的Subjects来使用,需要订阅然后接受.next的event。你也可以在你需要查看当前值的时候,直接去访问Variables的value属性,而不用去订阅它和不断的一直接收新event。

上代码:

func variable() {
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  var variable = Variable("A")
  
  // 3
  variable.value = "B"
  
  // 4
  variable.asObservable().subscribe {
    print("subscribe_1:", $0)
  }.disposed(by: disposeBag)

  // 5
  variable.value = "C"
  
  // 6
  variable.asObservable().subscribe {
    print("subscribe_2:", $0)
  }.disposed(by: disposeBag)
  
  // 7
  variable.value = "D"

}
variable() print output

调用测试方法 variable() 然后看看输出结果,我们来一步一步讲解:
1.创建一个DisposeBag
2.因为Variables它是对BehaviorSubjects的封装,所以它也需要默认初始值来创建,这里我们用了"A"
3.我们修改variable.value 为"B",这就相当于对Subject调用了onNext("B")方法,此时如果有已订阅者就可以收到.next的事件
4.通过asObservable()方法或者内部的那个Subject,然后订阅它,因为它具有跟BehaviorSubjects同样的特性,所以当一订阅它的时候,立刻就会收到一条上一条已发出的.next事件。可以看到输出打印了"subscribe_1 :next(B)"
5.再次修改value值为C,可以看到打印"subscribe_1 :next(C)",表明订阅1收到了新的这个.next事件
6.再次进行一次订阅,同理会立刻收到上一条发出的.next,可见打印输出了"subscribe_2: next(C)"
7.最后一次再修改value值为D,结果当然就是subscribe_1和subscribe_2都收到了.next(D)的事件并且打印了出来
8.最后重点看最后两个输出结果,subscribe_1和subscribe_2还接收到了completed事件,这是为什么呢?因为就像上面介绍里提到的,你不能手动给Variables发送completed或者error事件来结束它,而是当Variables要自己deallocate的时候他会自动的发出complete。因为我们这个Variable对象初始化在func variable()方法内,所以他的生命周期就被限制在了方法内,当这个方法全部执行完毕的时候,这个Variable对象就要被销毁了,所以它也就自动地向它的所有订阅者发出了completed事件。




至此RxSwift的最基本的一些概念就介绍完了,之后有时间时候会继续渐进的写文章介绍RxSwift或者RxCocoa。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 发现 关注 消息 RxSwift入坑解读-你所需要知道的各种概念 沸沸腾关注 2016.11.27 19:11*字...
    枫叶1234阅读 2,713评论 0 2
  • 最近在学习RxSwift相关的内容,在这里记录一些基本的知识点,以便今后查阅。 Observable 在RxSwi...
    L_Zephyr阅读 1,700评论 1 4
  • 首先,何为RxSwift? RxSwift是ReactiveX的Swift版本,一个响应式变成框架。传送门 开始之...
    cocoawork丶阅读 441评论 0 3
  • 今天4月13日,人间最美四月天中这样寻常的一天。昨日还是春风和煦,温润如玉。今日早上起来,外面却没来由地飘起小雨,...
    涟漪浅绿阅读 574评论 6 7
  • 《时间管理》课后心得 时间都去哪儿了,还没好好感受年轻就老了,时间都去哪了,还没好好看看你眼睛就花了;多么感人的歌...
    瑞成大哥阅读 443评论 2 4