现在我们学会了使用RxSwift和一些基本概念,现在我们去学习observables。
在这一章中,我们去创建几个例子练习订阅observables。实际使用可能有点模糊,但是我们可以获得更多的技巧和学习更多的observables类型。这些技巧将在本书中的下部分伴随着你!
Getting started
启动一个命名为RxSwiftPlayground的项目。已经用CocoaPods安装好了RxSwift库。打开Xcode,编译。
通过源文件选择SupportCode.swift文件,它包含以下helper函数示例:
public func example(of description: String, action: () -> Void) {
print("\n--- Example of:", description, "---")
action()
}
在这一章中你将使用这个函数来封装不同的使用列子,很快,您将看到如何使用这个函数。
但是在你深入之前,你可以好好思考一个问题:什么是observable?
什么是observable?
Observables是Rx的核心。你将花一些时间思考observables是什么,怎么创建他们,怎么使用。
你会看到“observable”、“observable sequence,” 和 “sequence” 交替使用。实际上,他们都是一样的。你甚至可以看成特殊从一个时间点到另一个时间点的“流”,尤其是用RxSwift处理不同的响应环境。“流”也指的是同样的事情,但在RxSwift我们称之为sequence不是stream
一些工作在序列中的事情。一个Observable就是一个序列,包含一些特殊功能。其中之一也是最重要的一点就是,是异步的。Observables产生事件,经过一段时间的发射的过程。事件可以包含值,如数字或一个自定义类型的实例,也可以是公认的手势,比如单击。
概念化这个最好的方法之一是使用图表(这只是在时间轴的标注)。
从左到右的箭头代表时间,圆圈里的数字代表序列里的元素。元素1将会发送,在某个时刻会通过,然后元素2和元素3将会发送。会花费多少时间?它可能贯穿observable的生命周期。让你了解到observable的生命周期。
Lifecycle of an observable
在前面的图表中,observable发送了3个元素。当observable发送一个元素,就已经知道了它的next事件。
下面是另一个图表,时间轴上有一个垂直的块表示observable的终点。
observable发送三个单击事件,然后结束。它将调用completed事件来终止。例如,也许单击在传递到view上被遗失了。observable已经终止,不会发送任何事件。这是正常的中断,然而,一些也可能出错。
上图中出现了一个错误,用红色的X表示。observable发送一个error事件,这和observable通过completed事件后正常中断没有什么区别。如果observable发送error事件,也会中断不会发送任何事件。
下面是快速回顾:
- observable发送next事件包含元素,会一直持续:
- 发送error事件中断,或者
- 发送completed事件中断。
- 一旦observable被中断,将不会在发送事件。
通过RxSwift源码看出,这些事件用枚举表示:
/// Represents a sequence event.
///
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
可以看出,.next事件包含Element。.error事件包含Swift.Error,**.completed **事件只是简单的终止不包含任何数据。
现在你知道observables是什么和做什么的了么,我们将创建observables在事件中来观察。
创建observables
在RxSwift.playground文件中添加如下代码:
example(of: "just, of, from") {
// 1
let one = 1
let two = 2
let three = 3
// 2
let observable: Observable = Observable.just(one)
}
上面的代码做的事情是:
1.定义一些你将用到的数字常量。
2.创建Int型的observable序列用just方法匹配one.
只是适当命名,因为它所做的是创建一个observable序列包含单个元素。只是Observable的一类方法。然而,在Rx中方法归类为“operators”。你可以猜猜哪个方法是正确的。
在example(of:)代码块的末尾添加下面代码:
let observable2 = Observable.of(one, two, three)
这一次你没有显式地指定类型。你可能会认为因为你给它几个整数,Int类型的Observable。Option-click observable2显示其推断的类型,你会发现这是一个Int的Observable,而不是一个数组。
这是因为operator需要一个可变参数的类型推断的元素传递给它。
如果你想创建一个数组的observable,你可以在of中传递数组。代码如下:
let observable3 = Observable.of([one, two, three])
Option-click observable3你将看到实际是一个[Int]的Observable。operator可以传递一个数组或单一元素,看起来可能有点奇怪。然而,数组也是单一元素,而不是内容。
另一个创建observables的操作是from。代码如下:
let observable4 = Observable.from([one, two, three])
from操作需要从数组中创建可见类型的observable,Option-click observable4你将看见Observable的类型是Int而不是[Int]。
此时控制台看起来什么都没有。因为你还没有打印,是时候开始订阅observables了。
订阅observables
作为一个iOS开发,你可能对NotificationCenter非常熟悉;向观察者广播notifications,与RxSwift 的Observables是不同的。下面是观察UIKeyboardDidChangeFrame的notification,处理代码段为:
let observer = NotificationCenter.default.addObserver(
forName: .UIKeyboardDidChangeFrame,
object: nil,
queue: nil
) { notification in
// Handle receiving notification
}
订阅 RxSwift中的observable非常相似;你可以用observable来订阅它。所以用subscribe()来取代addObserver()。不像NotificationCenter,和单例不同,每个observable都是不同的。
更重要的是,observable直到被订阅了才会发送事件。observable是一个序列的定义;在Swift库中subscribing更像是调用next()事件的迭代:
quence = 0..<3
var iterator = sequence.makeIterator()
while let n = iterator.next() {
print(n)
}
/* Prints:
0
1
2
*/
订阅是这个的改进版。你可以为每个可以发送事件的observable类型添加handlers。回忆observable的.next,.error, 和.completed事件。.next 事件将会传递参数到handler,.error事件包含错误实例。
看下面这个action,编写一个新的例子:
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
}
这和上一个例子相似,添加下面的代码段到例子中,订阅observable:
observable.subscribe { event in
print(event)
}
注意看xcode输出区域。
Option-click subscribe,你将看到一个携带Int型事件和无返回值的逃逸闭包,subscribe返回Disposable,稍后将介绍disposables。
打印结果为:
--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed
observable为每个元素发射.next事件,然后发射.completed事件最好中断。在工作中,你通常对.next事件发送的元素更感兴趣而不是事件本身。
看看如何使用它们,用下面的代码替换上面的订阅代码:
observable2.subscribe { (event) in
if let element = event.element{
print(element)
}
}
事件有一个element属性。是可选值,因为.next事件包含一个element。所以如果element如果不是nil的话你可以可选是否绑定。现在,只是打印出了elements,不是包含elements的事件,也不是.completed事件。
1
2
3
这是一个很好的案例,在RxSwift通常有捷径。这里有subscribe操作符为每个事件类型的observable发送next,error,completed**。用这段代码替换上一段:
observable2.subscribe(onNext: { element in
print(element)
})
现在你只处理.next事件,忽略其他,onNext闭包只接收.next事件元素作为参数,所以你不需要像之前那样去检索。
你知道了怎样用一个元素和多个元素创建observable。但是怎么用0个元素创建observable?empty操作符用0个元素创建空的observable序列;只会发射.completed事件。
例子如下:
example(of: "empty") {
let observable = Observable.empty()
}
observable必须定义一个特殊的类型因为不能推理出来,empty不会推理任何类型,必须自己定义。Void是一个很好的选择。添加下面的代码去订阅它:
observable.subscribe(
// 1
onNext: { element in
print(element)
},
// 2
onCompleted: {
print("Completed")
} )
这段代码将按一下顺序运行:
1.处理.next事件,和上个例子一样。
2..complete事件不包含元素,所以只是打印出来。
在控制面板,将看到empty仅仅发送.completed事件:
--- Example of: empty ---
Completed
empty observable怎么使用?当你想返回一个立即中断的observable时会很方便,或者只有0个值得时候。
和empty操作符相反的是,never创建的observable不会发射任何事件和中断。它可以被用来表示无穷的时间。示例:
example(of: "never") {
let observable = Observable.never()
observable
.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
}
) }
没有打印任何东西,连"Completed"都没有。你怎么知道它如何工作?保持好奇直到Challenge部分。
目前,我们只是看到了可见变量,但也有可能是一段值得范围来生成observable。示例:
example(of: "range") {
// 1
let observable = Observable.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)
})
}
一步一步分解:
1.用range操作符创建一个observable,需要一个开始值和一串连续的整数来生成。
2.对没个发送出来的元素计算和打印nthFibonacc数值
在第七章 “Transforming Operators.”中有一个比onNext更好的方法来转变发送的元素。
除了never()示例,到目前为止observables都是自动发射.completed事件和自然终止。这允许你专注于创建和订阅observables,但是对observables的深入也很重要。
Disposing and terminating
observable直到接收订阅之前都不会做任何事情。是订阅触发了observable了开始发射事件,直到发射.error或.completed然后自然中断。你可以手动取消订阅observable。示例:
example(of: "dispose") {
// 1
let observable = Observable.of("A", "B", "C")
// 2
let subscription = observable.subscribe { event in
// 3
print(event)
}
}
简短概述为:
1.创建observable。
2.订阅observable,用subscription接收返回值。
3.打印发射的事件。
调用dispose()取消subscription,在取消subscription之后,observable将会停止发射事件,示例:
subscription.dispose()
管理每个subscription会很糟糕,所以RxSwift包含DisposeBag类型。dispose bag 表示一次性的 —— 通常用.addDisposableTo()添加 —— 当dispose bag还未销毁之前它将为每个对象调用dispose()方法。示例:
example(of: "DisposeBag") {
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe { // 3
print($0) }
.addDisposableTo(disposeBag) // 4
}
disposable是怎么工作的:
1.创建dispose bag。
2.创建observable。
3.订阅和打印发射的事件。
4.为subscrib添加disposeBag。
这是常用的模式;创建和订阅一个observable然后立即将subscription添加到dispose bag。
为什么要一直使用disposables?如果忘记将subscription添加到dispose bag,或者当subscription完成后再调用dispose,或者一些其他方式造成observable在某点中断,你将可能造成内存泄漏。不要担心你忘记了;Swift编译器将会警告你没有使用disposables。
在前一个例子,创建了一个特殊的.next 事件的observables。另外使用create 操作符创建的observable的所有特殊事件都会发送到subscribers,示例:
example(of: "create") {
let disposeBag = DisposeBag()
Observable.create { observer in
}
}
create操作符携带了一个单一的参数叫subscribe。它的工作是为observable上的subscribe提供实现。换句话说,它定义了将要发送到subscribers所有的事件。 Option-click create。
subscribe是一个携带AnyObserver和返回值为Disposable的逃逸闭包。AnyObserver是一个方便在observable序列中添加将要被发送到subscribers的值的泛型类型,改变create的实现:
Observable.create { observer in
// 1
observer.onNext("1")
// 2
observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
}
一步一步解释为:
1.向observer中添加.next事件。onNext(:)是on(.next(:))的便利方法。
2.向observer中添加.completed事件。onCompleted是on(.completed)的便利方法。
3.向observer中添加.next事件。
4.返回disposable。
你认为第二个onNext参数“?”会被发送到subscribers吗?看看你的猜想是否正确,添加如下代码:
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
.addDisposableTo(disposeBag)
observable已经被订阅和实现了操作,用onNext传递默认参数和onError传递错误参数。结果是首先触发.next事件,然后打印出"Completed" 和 "Disposed" ,第二个.next 事件不会被打印出,因为observable发送的.completed事件 在打印它之前就已经终止了。
--- Example of: create ---
1
Completed
Disposed
如果在observer上添加一个error会发生什么?在上面示例中添加以下代码:
enum MyError: Error {
case anError
}
创建一个错误类型。添加到observer.onNext 和 observer.onCompleted之间:
observer.onError(MyError.anError)
observable发射一个错误然后终止:
--- Example of: create ---
1
anError
Disposed
如果你发射的不是.completed事件也不是.error事件,也没把subscription添加到disposeBag会发生什么?
注释掉observer.onError,observer.onCompleted, addDisposableTo(disposeBag),实现如下:
example(of: "create") {
enum MyError: Error {
case anError
}
let disposeBag = DisposeBag()
Observable.create { observer in
// 1
observer.onNext("1")
// observer.onError(MyError.anError)
// 2
// observer.onCompleted()
// 3
observer.onNext("?")
// 4
return Disposables.create()
}
.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
// .addDisposableTo(disposeBag)
}
这导致了内存泄漏!observable永远不会结束,disposable也永远不会释放。
--- Example of: create ---
1
?
如果你不想这个例子内存泄漏就取消.completed 事件的注释或把subscription添加到disposeBag。
Creating observable factories
更合理的是创建一个observable factories来给每个观察者提供新的observable而不是创建一个observable来等待订阅者。示例:
example(of: "deferred") {
let disposeBag = DisposeBag()
// 1
var flip = false
// 2
let factory: Observable = Observable.deferred {
// 3
flip = !flip
// 4
if flip {
return Observable.of(1, 2, 3)
} else {
return Observable.of(4, 5, 6)
}
}
}
这做了些什么?
1.创建了一个observable返回的布尔变量flip。
2.用deferred操作符创建了Int型的observable。
3.flip取反,当factory被订阅的时候。
4.根据flip的值返回不同的observables。
事实上,observable factory与常规的observable不能区分出来。添加这段代码来订阅factory4次:
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.addDisposableTo(disposeBag)
print() }
每次订阅factory,都会得到相反的observable。显示123,然后是456,每创建一个新的订阅者都会重复:
--- Example of: deferred ---
123
456
123
456