如今咱们已经了解到了一些基本概念, 就能够开始使用observables了.git
在这篇教程中, 咱们会编写几个建立和订阅observables的例子, Observable在刚开始使用的时候可能会有一些抽象, 但请放心, 随着咱们慢慢的深刻, 而且了解RxSwift中可用的Observable对象的许多类型, 这些问题都会慢慢迎刃而解的.github
注意: 如转载本文章, 请联系做者, 并给出文章的源地址, 本示例的代码能够在Observables下载编程
在本章中, 咱们将使用一个Xcode项目, 该项目已经写好了CocoaPods所需的东西, 直接打开终端, 去到当前目录而后输入:swift
pod install
复制代码
CocoaPods就会自动的把RxSwift和咱们的工程关联起来了.vim
注意: 该教程使用的是RxSwift 4.4.1版本, Xcode 10.3, 默认是Swift 5.0版本.数组
Observable是Rx的核心, 咱们将花一些事件讨论什么事Observable, 以及如何建立它们和使用它们.闭包
咱们将会看到**"Observable", "Observable sequence"和"sequence"在Rx中交换使用, 但实际上它们其实都是同样的. 咱们甚至偶尔会看到一个"stream", 特别是来自RxSwift的开发人员, 他们接触过不少不一样的编程环境. "Stream"其实也是指一样的东西, 可是在RxSwift中, 咱们都称它为序列, 而不是"stream", 在RxSwift**中...异步
…或者与序列有关的东西. Observable只是一个序列, 有一些特殊的功能, 其中一个功能——也是最实用的功能——异步(asynchronous). Observable对象在一段时间内产生事件, 这个过程称为发出(emitting). Observable的事件能够包含值, 好比说基础数据类型, 自定类型的实例, 也能够是手势, 好比点击等等.async
最简单的表现形式就是使用时序图, 它只是在时间轴上标记而已.函数
从作到右的箭头表示事件, 编号的圆圈表示序列的元素. 元素1将被释放, 通过一段时间以后, 元素2和元素3又会被释放掉, 若是要问这里大概须要多少事件.
它能够是Observable对象生命周期的任何一个点, 接下来咱们就看看Observable对象的声明周期.
在前面的时序图中, 咱们能够看到Observable发出的三个元素, 当一个Observable对象发出一个元素时, 它会在onNext中发出这个元素.
下面是另外一个时序图, 此次是一个条从头到结束的Observable事件线.
这个Observable发出了三个tap事件, 最后就结束. 这个过程咱们称为completed事件, 由于它已经被终止. 例如咱们可能在监听一些事件, 而后完成了.
重要的是Observable对象已经被终止了, 不会再次发出任何的东西, 这是正常的结束, 固然, 有时候事情并非咱们想象的那样顺利, 也会有出错的状况.
在这个时序图中发生了一个错误, 图里用红色的X表示, Observable发送了一个error的事件, 这与Observable在正常状况下完成了事件而且终止没有什么不一样, 由于当Observable发出了一个error事件, 那么它也会终止, 而且不会再发出任何东西.
下面有一个快速的介绍:
以RxSwift的源代码为例, 这些事件表示为枚举:
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
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
事件包含Swift .Error, .completed
是不包含任何数据的中止事件.
如今咱们已经了解了什么是Observable, 以及它能够作什么, 接下来咱们将建立一些Observable来看看它们的行为.
从当前文章回到咱们的项目工程, 而且替换下面的代码:
@IBAction func createObservable(_ sender: Any) {
print("--- just, of, from ---")
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable = Observable<Int>.just(one)
}
复制代码
下面是咱们在上面代码中所作的:
just的名字和它所作的事情很是的贴切, 由于它就是建立一个只包含单个元素的Observable, 只是Observable的其中一种类型方法, 然而在Rx中, 这种方法被称为操做符(Operators).
接下来咱们继续下一个操做符:
let observable2 = Observable.of(one, two, three)
复制代码
此次咱们没有明确的指定类型, 在猜想它有多是**[Int]类型的Observable**对象.
按住option + 鼠标左键点击一下, 咱们能够到它是一个Int类型的Observable对象, 不是一个数组:
这是由于of操做符能够接受可变参数, Swift可以根据它推断出Observable对象的类型.
若是想要建立一个Observable的数组, 咱们能够简单的将一个数组传递进去, 添加下面的代码到工程中:
let observable3 = Observable.of([one, two, three])
复制代码
再次按住option + 鼠标左键点击一下, 咱们就能够看到它确实是一个**[Int]的Observable对象, just操做符也能够将数组做为它的单个元素, 这看起来可能有些奇怪, 然而在just**操做符中, 数组只是单个元素, 不是它的内容.
咱们还能够用另外一个操做符from来建立Observable对象, 在工程中添加下面代码:
let observable4 = Observable.from([one, two, three])
复制代码
from操做符能够从数组中建立单个的Observable元素, 按住option + 鼠标左键点击一下, 咱们会看到它是一个Int类型的Observable, 而不是**[Int]**, 注意一下, from操做符只接受数组.
如今咱们尚未进行操做, 因此Xcode中的控制台目前看起来光秃秃的, 除了介绍了上面三个操做符以后, 咱们并无实际操做什么, 接下来, 咱们就来订阅一下这些Observable对象, 来点实际的.
做为一个iOS开发人员, 咱们可能熟悉NotificationCenter, 它会向订阅者发送通知广播, 这和RxSwift的Observable对象不一样. 下面是一个UIKeyboardDidChangeFrame通知的观察者的例子, 后面有一个处理相关操做的Block:
let observer = NotificationCenter.default.addObserver(
forName: UIResponder.keyboardDidChangeFrameNotification,
object: nil,
queue: nil) { notification in
// Handle receiving notification
}
复制代码
订阅一个RxSwift的Observable对象很是的简单, 咱们用观察者去订阅它就能够了, 就至关于把上面的addObserver()
替换为subscribe()
, 与NotificationCenter不一样, Rx中的每一个Observable对象都是不同的, 开发人员一般只会用到它的.default
单例.
更重要的是, 一个Observable对象在没有订阅者的状况下是不会直接就发送事件或者执行任何操做.
记住, Observable对象其实是一个序列的定义, 订阅一个Observable对象实际上更像在Swift标准库的迭代器调用next()
.
let sequence = 0..<3
var iterator = sequence.makeIterator()
while let n = iterator.next() {
print(n)
}
/* Prints: 0 1 2 */
复制代码
不过, 咱们订阅Observable要比这个更加的精简, 咱们还能够为Observable发出每种事件类型添加处理操做.
回想一下, 一个Observable会发出的.next
, .error
和.completed
事件. .next
事件将发出的元素传递给应用程序继续处理, .error
事件则是一个包含错误信息的实例.
来点实际的代码, 将下面的代码添加到咱们示例当中:
@IBAction func subscribe(_ sender: Any) {
print("\n--- subscribe ---")
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
}
复制代码
这和前面的示例相似, 只是此次咱们使用的是of操做符, 如今在这个例子的底部订阅Observable, 而且在闭包里添加一段操做代码:
observable.subscribe { event in
print(event)
}
复制代码
按住option + 鼠标左键点击一下subscribe
操做符, 咱们会看到它接受一个转义的block, 该闭包接受Int类型的事件, 而且不返回任何内容, subscribe会返回一个能够丢弃的闭包, 以管理资源, 稍后咱们将会涉及到Disposable.
触发事件后, 咱们会看到输出的结果:
--- subscribe ---
next(1)
next(2)
next(3)
completed
复制代码
Observable对象为每一个元素发出.next
事件, 而后再发出.completed
事件表示终止, 当咱们使用Observable时, 一般会对.next
事件发出的元素比事件自己更感兴趣.
那么咱们如何去访问呢? 咱们用下面的代码将以前的订阅代码给替换掉:
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
复制代码
事件是具备元素属性的, 而且是一个option, 由于.next
事件有一个元素, 只要元素不为nil, 则可使用option绑定来展开元素, 如今咱们再次触发, 就会看到控制台上只打印元素, 并不会打印包含元素的事件, 也不会输出.completed
事件 .
1
2
3
复制代码
因为这种模式使用的很是频繁, 因此RxSwift为了让咱们更加的简单使用, 还提供了一种操做符, 能够包含Observable对象发出每一种类型的事件: next, error和completed.
用下面的代码来代替以前的订阅代码:
observable.subscribe(onNext: { element in
print(element)
})
复制代码
如今, 咱们只须要处理.next
事件的元素, 其余的内容则会被忽略掉. onNext闭包接收.next
事件的元素做为参数, 因此咱们不用再像以前那样子手动的去检索元素.
如今咱们已经了解了如何建立一个或者多个元素的Observable对象, 那么零元素的Observable呢? RxSwift也考虑到了这种状况, 由于提供了一个.empty
操做符, 它只会发出.completed
事件.
添加下面的例子到咱们的工程中:
@IBAction func empty(_ sender: Any) {
print("\n--- empty ---")
let observable = Observable<Void>.empty()
}
复制代码
若是咱们没有办法去推断类型, 那么则必须得显式的声明Observable的特定类型, 而empty就是没有推断的内容, 因此必须显式的声明定义类型.
在这种状况下咱们使用Void比其余类型都要好, 将下面的代码添加到工程中:
observable.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
})
复制代码
依次来解释一下:
.next
事件, 就像咱们在前面的示例中那样..completed
事件不包含任何的元素, 这里只打印一条消息在控制台中, 咱们会看到empty只发出.completed
事件:
--- empty ---
Completed
复制代码
关于这个.empty
有人可能会有疑问, 这个操做符能作什么? 仔细的想一下, 若是咱们想返回一个当即终止或者有意为零的Observable元素时, 那么它就很是的有用了.
与这个empty操做符相反的就是never操做符, 是用来建立一个永远不会发出任何东西, 也永远不会终止的Observable对象, 它能够用来表示无限的持续时间, 把下面的例子添加到工程:
@IBAction func never(_ sender: Any) {
print("\n--- never ---")
let observable = Observable<Any>.never()
observable.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
})
}
复制代码
一旦咱们出发这个事件, 咱们只会看到标题头, 而后.next
事件和.completed
事件是不会输出任何信息的.
到目前为止, 咱们使用的都是显式元素或者有值的Observable, 除此以外, 也能够生成一个范围的Observable.
把下面的例子添加到工程:
@IBAction func range(_ sender: Any) {
print("\n--- range ---")
// 1
let observable = Observable<Int>.range(start: 1, count: 10)
observable
.subscribe(
onNext: { i in
// 2
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) / 2.23606).rounded())
print(fibonacci)
})
}
复制代码
继续来拆解:
实际上, 有一个比.onNext
更好的处理转换元素的操做符, 这个咱们会在将来的教程中会学习到.
到目前为止, 除了never()
示例以外, 咱们一直都在处理Observable对象, 它们会自动的发出.completed
事件并终止Observable序列. 这会使得咱们将重点放在建立和订阅Observable的机制上, 虽然这样子很方便, 但会掩盖了咱们进一步了解Observable, 接下来, 咱们细说一下Observable的处理和终止.
有一件事情咱们须要记住, 在Observable对象未接收到订阅以前, 是不会作任何事情的, 订阅是用来触发Observable对象的工做, 而后才会使Observable发出事件, 直到它发出.error
或.completed
事件并终止为止, 除此以外, 咱们也能够经过取消Observable对象的订阅, 而后终止Observable对象.
把下面的代码添加到工程中:
@IBAction func dispose(_ sender: Any) {
print("\n--- dispose ---")
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe { event in
// 3
print(event)
}
}
复制代码
简单的介绍:
若是咱们要显式的取消订阅, 咱们会调用dispose()
. 再次说一下, 取消订阅或处理完订阅以后, 当前示例中的Observable将会中止发出事件.
将下面的代码添加到例子中:
subscription.dispose()
复制代码
可是这种单独管理每一个订阅是很是的繁琐的, 因此RxSwift提供了一个统一管理的DisposeBag类型, 它可使用添.dispose(by: )
方法将subscribe添加到disposeBag中, 每立即将要释放Observable对象时, 都会自动的去调用dispose()
方法.
下面的代码添加到工程中:
@IBAction func disposeBag(_ sender: Any) {
print("\n--- DisposeBag ---")
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe {
// 3
print($0)
}
// 4
.disposed(by: disposeBag)
}
复制代码
解释一下代码的工做流程:
这将会是咱们之后最经常使用的模式, 建立并订阅一个Observable, 而后当即将subscribe添加到disposeBag中.
为何要那么麻烦使用disposeBag?
若是咱们忘了向disposeBag添加订阅, 或者在订阅完成以后忘了手动添加dispose, 那么就会形成本该结束的Observable对象没有结束, 致使内存泄漏.
幸亏的是, Swift编译器会提醒咱们没有使用disposables.
在前面的示例里, 咱们都是建立Observable对象, 而后再使用.next
来输出元素, 如今咱们来点不同的, 使用create操做符, 这个操做符容许咱们将指定的Observable对象向订阅者发出全部事件的另外一种方式.
把下面的示例添加到代码中:
@IBAction func create(_ sender: Any) {
print("\n--- create ---")
let disposeBag = DisposeBag()
Observable<String>.create { observer in
}
}
复制代码
create操做符接受一个名为subscribe的参数, 它工做是提供对Observable调用subscribe的实现, 换一句话说它定义了将发送给订阅者的全部事件, option + 鼠标左键点击一下:
subscribe参数是一个转义闭包, 它接受AnyObserver并返回一个逃逸闭包. AnyObserver是一种泛型的类型, 它能够很是方便的将值添加到一个Observable序列中, 而后将该序列发送给订阅者.
将create的实现更改成下面的代码:
Observable<String>.create { observer in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
}
复制代码
代码详情:
.next
事件到Observable, .onNext(_:)
是on(.next(_:))
的快捷方法..completed
事件添加到Observable中, 和上面同样, .onCompleted
是on(.completed)
的快捷方法..next
事件到Observable.注意: 在最后一步, 咱们返回的是disposable, 这看起来感受会很奇怪, 请记住, subscribe操做符返回一个表示订阅的
Disposable.create()
则是一个空的disposable, 则不会有其余的影响, 但有些disposables就另外说了.
这里提一个问题, 你认为第二个.onNext("?")
元素会发给订阅者吗? 为何呢?
咱们来试试, 看看你个人猜想是不是正确的, 添加下面的代码到create实现以后:
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed")}
)
.disposed(by: disposeBag)
复制代码
如今咱们已经订阅了Observable, 并实现了全部的处理细节, onNext和onError使用了默认参数**$0输出元素和错误信息, 可是咱们发现结果则是.next
的第一个事件, 接下来就是"Completed"和"Dispose"**.
第二个.next
事件不会打印, 回想一下这其中的缘由咱们也知道, 由于Observable发出了一个.onCompleted
事件而后就终止序列了, 因此后续的.onNext
事件则不会再发出.
--- create ---
1
Completed
Disposed
复制代码
若是咱们向Observable添加错误会发生什么样的事情呢? 在示例的顶部添加下面的代码:
enum MyError: Error {
case anError
}
复制代码
接下来咱们在create中将observer.onCompleted
替换成observer.onError
:
observer.onError(MyError.anError)
复制代码
Observable则会发出错误信息, 而后终止序列:
--- create with error ---
1
anError
Disposed
复制代码
若是咱们既没有发出.completed
, .error
事件, 也没有将subscribe添加到disposeBag中, 那么会发生什么状况呢?
下面是完整的实现:
@IBAction func createWithMemoryLeak(_ sender: Any) {
enum MyError: Error {
case anError
}
print("\n--- create with memory leak ---")
let disposeBag = DisposeBag()
Observable<String>.create { observer in
observer.onNext("1")
observer.onNext("?")
return Disposables.create()
}
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed")}
)
}
复制代码
恭喜你, 你刚刚建立了一个无限的Observable序列, 该Observable永远都不会结束.
--- create with memory leak ---
1
?
复制代码
若是你无法忍受这个示例处于内容泄漏状态, 能够把刚刚咱们删掉的.completed
, .error
事件添加回去, 而且将subscribe添加到disposeBag中.
与其建立一个等待订阅者的Observable对象, 不如建立一个Observable工厂, 为每一个订阅者提供一个新的Observable对象.
把这个新例子添加到工程中:
@IBAction func deferred(_ sender: Any) {
let disposeBag = DisposeBag()
print("\n--- deferred ---")
// 1
var flip = false
// 2
let factory: Observable<Int> = Observable.deferred {
// 3
flip.toggle()
// 4
if flip {
return Observable.of(1, 2, 3)
} else {
return Observable.of(4, 5, 6)
}
}
}
复制代码
解释一下代码:
从外部看, 一个Observable对象工厂和普通的Observable没啥区别, 添加下面的代码到示例中:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
复制代码
每订阅一次factory就会获得相反的Observable的value, 先获得123, 而后再获得456, 每有一个新的订阅都会重复这个模式:
--- deferred ---
123
456
123
456
复制代码
Observable的特性, 要比常规的Observable行为范围更小, 它们的使用是可选的, 咱们能够在任何一个使用Observable特性的地方使用一个常规的Observable对象, 它们的目的是提供一种方式来更清楚的向代码阅读者或者是API的使用者传达咱们的意图, 使用Observable的特性, 可使得咱们的代码更加的直观.
RxSwift有三种特性: Single, Maybe和Completable, 在不了解它们的状况下, 你能猜到它们是作什么的吗?
Singles只会发出.success(value)
或.error
事件, .success(value)
其实是.next
和.completed
事件的组合, 这对于使用一次性进程很是有用, 一次性进程要么成功输出一个value, 要么就失败, 例以下载数据或从硬盘加载数据. Completable只会发出.completed
或.error
事件, 它不会发出任何的值, 只关于某个操做成功完成或者是失败了, 例如文件的写入, 咱们就可使用completable.
最后, Maybe是Single和Completable的组合, 它能够发出.success(value)
, .completed
或.error
事件, 若是咱们须要实现一个可能会成功或者失败的操做, 而且在成功的时候返回一个值, 那么Maybe就是咱们所须要的...
咱们会在将来的教程中学习更多有关于RxSwift的特性, 如今咱们来运行一个基本的示例, 在工程中, 咱们有一个hello.text文件, 咱们将使用Observable的特性来加载并输出这个文本内容.
把下面的例子添加到咱们的工程中:
// 1
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}
// 2
private func loadText(from name: String) -> Single<String> {
// 3
return Single.create { single in
}
}
复制代码
解释一下咱们要在代码中作的事情:
在create闭包中添加下面的代码来实现咱们须要的操做:
// 1.
let disposable = Disposables.create ()
// 2
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}
// 3
guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}
// 4
guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}
// 5
single(.success(contents))
return disposable
复制代码
咱们解释一下这段代码:
.fileNotFound
和disposable.unreadable
和disposable..encodingFailed
和disposable如今咱们能够来使用这个函数了, 将下面的代码添加到工程中:
// 1
loadText(from: "hello")
// 2
.subscribe {
// 3
switch $0 {
case .success(let string):
print(string)
case .error(let error):
print(error)
}
}
.disposed(by: disposeBag)
复制代码
在这里咱们作的操做是:
loadText(from:)
, 而后传递你要读取的文件名.如今咱们应该能够看到控制台中输出的文本内容:
--- single ---
Hello RxSwift
复制代码
若是咱们将文件名换成其余的话, 应该会打印出未找到文件的错误信息.
剩下Completable和Maybe这里就不作介绍了, 工程中有对应的代码:
熟能生巧, 咱们将实践在本章中所学到的只是来完成这些挑战, 而且得到更多关于使用Observable对象的知识.
在前面的never操做符实例中, 没有输出任何的内容, 那是由于咱们输出的内容是添加到subscribe之中, 若是咱们能添加到在这以前, 而且再将subscribe添加到disposeBag, 那咱们就能够看到消息的输出了.
还有一个很是实用的操做符, 它容许咱们在不印象Observable对象的状况额外添加一些咱们想作的事情.
do操做符将容许咱们添加附加效果, 也就是说, 无论这个Observable对象会发出什么样的事件, do只是将事件传递连接到下一个操做符中, do这个操做符还包含一个onSubscribe的闭包, 咱们将会在这里处理额外的事情.
使用do操做符的方法是do(onNext:onError:onCompleted:onSubscribe:onDispose)
, 你能够在这里实现任何你想作的事情.
咱们将使用do操做符改造一下咱们的never示例, 在改造的时候, 千万别忘了把subscribe添加到disposeBag中.
除了刚刚咱们提过的do操做符能够做为Rx的调试方式以外, 还有一种操做符叫作debug操做符, 它将为一个Observable对象打印出每一个事件信息.
它有几个可选参数, 可能最有用的就是包含一个标识符字符串, 该字符串会打印出来, 在复杂的Rx链中, 咱们能够在多个位置添加debug操做符, 这能够帮助咱们区分打印出来的源subscribe.
经过使用debug操做符替换do操做符, 并为其提供一个字符串做为标识符.
1.当Observable事件完成时会发出Completed事件, 表明事件结束;
2.当Observable在未完成事件前发生了错误, 该事件也会结束, 而且抛出error事件;
3.当Observable的事件完成了以后就不会再发出任何事件了.
subscribe()
函数订阅Observable.create()
操做符建立的Observable, 在处理完事件以后, 必定要调用一下.onCompleted()
或者是.onError()
, 而且在subscribe时必定要调用disposed(by: DisposeBag)
, 不然Observable永远不会被释放, 形成内存泄漏..success(value)
或.error
事件, 而.success(value)
其实是.next
和.completed
事件的组合.completed
或.error
事件, 不会发出任何值.success(value)
, .completed
或者.error
事件