RxSwit中最重要的一个概念叫做Observable
,我们可以把他翻译为序列、可观察序列等等
流、序列、可观察序列,都是一样的东西。就是一个可以持续发出事件的东西。
在Rx中 方法也被称作为 操作符
怎样创建一个信号流
-
just 方法
let observable = Observable.just(1)
just是Observab了的一个静态方法
用这个方法创建了一个只发射一个元素的信号流
-
of 方法
Observable.of(1, 2, 3, 4)
just方法只能接受一个元素,如果有多个元素需要用过信号流发送出去,就可以使用 of 方法
使用场景:如果要对一个数组进行遍历 就可以通过 of 方法来代替 for in 循环
-
from 方法
Observable.from([1, 2, 3, 4])
from方法也是创建了一个信号流,但是它接受的是一个数组,然后把这个数组中的元素逐个发送出去
使用场景:如果传过来的就是一个数组,而不是一个一个元素,要进行遍历的时候就可以使用 from 方法生成的信号流
怎样监听一个信号流
在没有RxSwift 我们很熟悉 NotificationCenter,在通知模式中,我们想要监听一个通知会通过addObserver()监听指定的通知。
在RxSwift中和上面很相似,只不过把addObserver()替换成了subscribe()
但是和通知不一样的是,通知我们一般使用的是它的单例,而RxSwift的每一个信号源都是单独的。
如果一个信号没有被订阅的时候,它是不会发送事件的,内部的操作也不会执行的。
监听操作就像是在信号源和观察者之间建立一条通道,监听之后,才会有这条通道,信号源才会通过通道发送事件过来。
这个事件发送就像是一个流。我们可以处理这个事件流中的每一个事件。
通过 subscribe()
方法就可以监听一个信号流,下面是一个示例
let observable = Observable.of(1, 2, 3)
observable.subscribe{ event in
print(event)
}
上面的代码会输出
next(1)
next(2)
next(3)
completed
点击subscribe方法可以看到,它的闭包的参数是一个 Event<Int> 类型的值,每次信号流发送信号,都把值放到Next事件包起来然后发送出来
但是通常我们使用的都是内部包起来的值 ,所以一般可以通过下面方式进行使用
observable.subscribe { event in
if let value = event.element {
print(value)
}
}
Event 有一个可选性的 element属性,因为只有 Next 事件才会有element,所以可以通过解包来拿到内部的值
因为事件的订阅是经常使用的,每次进行解包有些麻烦,所以Rx提供了一个快捷方法,对于每种事件都有一个操作符进行监听
observable.subscribe {
onNext: { element in
print(element)
},
onCompleted: {
print("completed!")
}
}
信号流的终止和释放
再提一次:一个信号源如果没有观察者监听,它不会做任何事。
是这个监听的动作触发了信号源的工作,让它一直发送事件,一直到发送了一个error或completed事件才会终止这个信号(信号源和观察者之间就断开了通道,后续的事件就不会受到了)。
除了通过发送 error 或 completed 事件,我们也可以通过取消监听来手动来销毁这个信号源,从而终止事件流。
写一个代码看一下信号流的终止
let observable = Observable.of("A", "B", "C")
let subscription = observable.subscribe { event in
print(event)
}
代码解释
创建了一个字符串的信号流
监听了这个信号。同时,将这个信号源的返回值Disposable 用一个常量subscription保存了起来
在闭包里面打印每一个事件
上面的代码并没有将这个信号源进行释放。也就是说这个信号流的所有信号都发送完了,但是在监听者和信号源之间的通道还存在。所以这里是没有意义的。它也会带来内存泄漏的问题。
如果要主动的取消订阅,可以让 销毁器(Disposable类型的subscription) 通过调用 dispose() 方法。当释放了这个信号源之后,它就不会再发送任何事件了。
subscription.dispose()
单独的处理每一个信号的订阅的销毁太麻烦,所以RxSwift提供了一个 DisposeBag
类型
一个DisposeBag 包含了所有的需要等待释放的 disposable
通常通过 disposed(by:)方法将订阅者添加到 这个DisposeBag包中,这个包在释放的时候,会对包中的每一个 订阅者(disposable) 调用 dispose() 方法进行终止信号流的操作。
所以一个通常的模式就是这样
let disposeBag = DisposeBag()
Observable.of("A", "B", "C").subscribe{
print($0)
}.disposed(by: disposeBag)
注释
先创建一个释放者包
创建一个信号流
监听这个信号流,并打印这发送的事件通过默认参数$0
将返回的disposable添加到释放包里面去
问题来了:这个销毁器是哪里来的?为什么通过subscribe()方法建立了监听之后就可以得到?dipose()方法执行了什么?。
这就要从信号最原始的创建方法 create() 来看
create 方法
之前看过提供的几个创建信号源的方法,都是自动就把事件都发出去了,我们没有主动的控制事件的发送。通过 create 方法就可以手动的发送所有可以使用的事件出去。
create的一个简单示例就是下面这样:
Observable<String>.create { observer in
observer.onNext("1")
observer.onCompleted()
return Disposables.create()
}
create方法的参数是一个闭包,闭包的形参名是 subscribe。那这个闭包什么时候执行。
从create的方法的代码注释中可以看到,这个闭包是 subscribe() 方法的实现。
所以就是说,它是在subscribe()方法执行的会被执行。
也就解释了为什么 一个信号如果没有产生订阅,它是不会执行任何操作的。
接下来 就要看一下这个闭包的参数和返回值
闭包接收一个AnyObserver参数,返回一个Disposable
我们上面介绍信号流的终止的时候看到了 subscribe() 的返回值是一个Disposable ,就是这里创建并返回的这个 Disposable。
AnyObserver 是一个泛型,它的作用就是把将要发送给订阅者的 值 添加到这个信号流中。
onNext() 是 on(.next(_:)) 的一个快捷方式
最后返回一个disposable 表示在这个信号流终止或销毁的时候要做的事情,在这个例子里面没有要清理的东西,所以返回了一个空的disposable
我理解这个 Disposable 和 Observable observer是没有什么关系的。它要做的是一些额外的资源的清理。
如果不发送 completed 或 error 这个信号流是不会停止的,也就不会执行 disposable 的,这个信号就不会被释放。(就是观察者和信号源之间的通道不会断开)
还有哪些方法可以创建信号流
-
empty 方法
let observable = Observable<Void>.empty() observable.subscribe { onNext: { element in print(element) }, onCompleted: { print("completed!") } }
上面的示例会直接输出 completed!
Observable.empty()方法会创建一个空的信号流,它会直接发送complete事件
什么时候会使用 empty()方法创建信号流 :当需要通过一个信号来告诉某个事情完成的时候可以考虑通过empty()创建一个空信号
-
range 方法
Observable.range(start: 1, count: 10).subscrbe( onNext: { value in print(value) } )
range方法会创建一个信号流,这个信号流会从start的值开始,发送信号,每次的信号值是上一次的值加1
当需要快速生成一个序列的时候,可以考虑使用 range 方法
-
never 方法
never创建出来的信号源,不会发送任何的事件。当监听之后,就会在监听者和信号之间建立一条通道,但是不会有任何的事件发送。
具体的使用场景还没找到。
特殊信号
和普通的信号流相比,这些特殊信号的可操作能力更小。通常他们只有一种能力可以执行。
在任何使用特殊信号的地方都可以用普通信号代替。
这些特殊信号就是为了简化代码
在RxSwift中先介绍三种特殊信号: Single
、 Maybe
、Completable
-
Single
Single只会发出 success(value) 或 error(error) 事件。
success(value)实际上是 next 和 completed的组合事件。这个信号的特点是会将最终的状态和结果值都发送出来
使用场景:比如对一次数据处理想要得到最终的处理状态和结果(比如数据下载,最终需要的状态和下载完成的结果、或者比如从硬盘读取一个文件,我们最终需要的是读取的结果和读取的文件内容)。就可以使用Single
// 定义文件读取错误时的错误类型
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailded
}
// 定义一个文件读取的方法
func loadDiskFile(path: String) -> Single<String> {
return Single<String>.create {
let disposable = Disposables.create()
guard let path = Bundle.main.path(forResource: path, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}
guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}
guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}
single(.success(contents))
return disposable
}
}
// 通过Single获取结果
loadDiskFile("sample").subscribe(
switch $0 {
case .success(let content):
print(content)
case .error(let error):
print(error)
}
).disposed(by: DisposeBag())
-
Completable
Completable 只会发送一个 completed 或 error(error)事件
它发送的这个事件不会携带值出来
如果只关心操作的结果,就可以考虑使用 Completable 信号
使用场景:向硬盘中写一份数据操作,我们只需要关心数据有没有写入成功。这个操作完成不需要向我们返回什么数据,就使用使用Completable
-
Mayby
Maybe是Completable和Single组合起来的一个操作符,它既可以像Single一样发送结果的状态和结果值 success(value), error(error),也可以只发送结果状态 completable
-
debug 操作符
在调试Rx代码的时候想看看代码执行的bug,可以使用 debug 操作符
通过这个操作符就可以打印每个事件的信息
这个方法有几个可选的参数,可能最有用的是可以让我们添加一个唯一字符串在输出的时候可以一起输出出来
后面还会有更多的特殊信号
上面一部分的小总结
到这里,我们在上面介绍的什么是信号流、怎么创建信号,怎么监听信号和怎么在信号释放的做一些事情。
但是上面的这些信号本质是只读的,我们只能监听这些信号,然后得到这些信号产生的事件。(或者说这些信号更像是一次性的,它处理完一些事情后,就把信号发送出来,然后这个信号的作用就结束了。)
在开发的过程中可能我们更通用的需求是:在运行的时候手动的添加一个信号到信号流中发送给监听者。
这就是下面要介绍的 Subject