Observable
Observable
ObservableType协议(Observable
简单。一个Observable只能发射(并且观察者可以接收)三种类型的事件:
next 事件:携带最新的(或下一个)数据值的事件。这是观察者接收值的方式。
completed 事件:此事件成功终止事件序列。它意味着Observable成功地完成了它的生命周期,不会发出任何其他事件。
error 事件:可观察到的以错误终止,不会发出其他事件。
当谈论随着时间推移而发出的异步事件时,您可以在时间线上形象化一个observable整数序列,如下所示:
一个Observable能发出的三种可能事件的简单契约就是Rx的一切。因为它是如此通用,所以您可以使用它来创建甚至最复杂的应用程序逻辑。
由于observable契约没有对Observable或观测者的性质做任何假设,因此使用事件序列是最终的解耦实践。您不需要使用委托协议,也不需要注入闭包来允许类之间进行对话。
为了了解一些现实生活中的情况,您将看到两种不同的observable序列:有限序列和无限序列。
有限observable序列
一些observable序列发出 零个,一个,或多个值,并且在以后的某个时刻,要么成功停止,要么因为错误停止。
在iOS应用程序中,考虑从Internet下载文件的代码:
•首先,开始下载并观察进来的数据。
•然后,随着文件的每一部分进来,您会重复接收数据块。
•如果网络连接中断,下载将停止,连接将超时并出现错误。
•或者,如果代码下载了文件的所有数据,它将成功完成。
这个工作流程准确地描述了一个典型的observable的生命周期。请看下面的相关代码:
API.download(file: "http://www...")
.subscribe(onNext: { data in
... append data to temporary file
},
onError: { error in
... display error to user
},
onCompleted: {
... use downloaded file
})
API.download(file:)返回一个observable实例,该实例在数据块通过网络过来时发出数据值。
你可以通过提供onNext闭包来订阅next事件。在这个下载的例子中,您将数据附加到存储在磁盘上的临时文件中。
最后,为了处理一个已完成的事件,您提供了一个onCompleted的闭包,在这个闭包中您可以推出一个新的视图控制器来显示下载的文件或应用逻辑指示的任何其他内容。
无限observable序列
不同于文件下载或类似的活动,它们应该自然地或强制地终止,还有其他的序列是无限的。通常,用户界面事件是无限的可观察序列。
例如,考虑一下对应用程序中的设备方向更改做出反应所需的代码:
•将类作为观察者添加到uideviceOrientationDidChange通知中。
•然后需要提供方法回调来处理方向更改。它需要从uidevice中获取当前方向,并相应地对最新值做出反应。
这个方向变化的序列没有一个自然的结束。只要有设备,就可能有一系列的方向改变。此外,由于序列实际上是无限的,所以在开始观察它时,总是有一个初始值。
用户可能从未旋转其设备,但这并不意味着事件序列被终止。它只是意味着没有事件发出。
在Rxswift中,可以编写这样的代码来处理设备方向:
UIDevice.rx.orientation
.subscribe(onNext: { current in
switch current {
case .landscape:
... re-arrange UI for landscape
case .portrait:
... re-arrange UI for portrait
}
})
uidevice.rx.orientation是一个虚构的控件属性,它可以生成一个observable
Operators 操作符
ObservableType和Observable类的实现包括许多方法,这些方法抽象了异步工作的独立部件,可以组合在一起实现更复杂的逻辑。
因为它们是高度解耦和可组合的,所以这些方法通常被称为运算符。由于这些运算符大多采用异步输入,只生成输出而不产生副作用,因此它们可以很容易地组合在一起,就像拼图块一样,并努力构建更大的画面。
例如,以数学表达式(5+6)*10-2为例。
以一种明确的、确定性的方式,您可以将运算符*、()、+和-按它们预先定义的顺序应用到作为它们输入的数据块上,获取它们的输出并继续处理表达式,直到解决它为止。
以某种类似的方式,您可以将rx运算符应用于Observable发出的的输入片段,以确定地处理输入和输出,直到表达式解析为最终值,然后使用该值来产生副作用。
以下是关于观察方向变化的前一个示例,调整为使用一些常见的RX操作符operators:
UIDevice.rx.orientation
.filter { value in
return value != .landscape
}
.map { _ in
return "Portrait is the best!" }
.subscribe(onNext: { string in
showAlert(text: string)
})
每次uidevice.rx.orientation生成一个.landscape或.portrait值时,rx都会对发出的数据应用几个operators操作符。
首先,筛选器filter将只允许通过不是.landscape的值。如果设备处于.landscape模式,则不会执行订阅代码,因为筛选器filter将禁止这些事件。
对于.Portrait值,map操作符将获取方向类型输入并将其转换为字符串输出-文本“Portrait is the best!”.
最后,使用subscribe可以订阅产生的next事件,这次带有一个字符串值,并调用一个方法在屏幕上显示带有该文本的提示框。
这些运算符也是高度可组合的-它们总是将数据作为输入并且输出结果,因此您可以通过许多不同的方式轻松地将它们链接起来,从而实现比单个运算符本身所能做的更多的功能!
在阅读本书的过程中,您将了解到更复杂的运算符,这些运算符抽象了更多涉及异步工作的部分。
Schedulers 调度器
调度器Schedulers是RX中等同于调度队列的调度程序——仅使用steroids,而且更容易使用。
RXSwift附带了许多预定义的schedulers调度器,它们涵盖了99%的用例,希望这意味着您永远不需要创建自己的schedulers调度器。
事实上,本书前半部分中的大多数示例都非常简单,通常处理观察数据和更新用户界面,因此在介绍了基础知识之前,您根本不会研究schedulers调度器。
也就是说,schedulers调度器非常强大。
例如,可以指定要观察SerialDispatchQueueScheduler上的next事件,它在给定的队列上使用GCD串行的运行你的代码。
ConcurrentDispatchQueueScheduler将会并发的运行你的代码。
OperationQueueScheduler将会允许你在给定的NSOperationQueue上调度你的订阅。
由于Rxswift,您可以在不同的schedulers调度器上调度同一订阅的不同工作,以获得最佳性能。
Rxswift将充当订阅(在下面的左侧)和调度器(在右侧)之间的调度员,将工作片段发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。
要阅读此图,按照序列中彩色的工作,它们被调度(1,2,3)给不同的调度器schedulers。例如:
•蓝色网络订阅从一段代码(1)开始,该代码在基于自定义NSOperation的调度器scheduler上运行。
•此块的数据输出用作下一个块(2)的输入,该块在一个并发后台GCD队列上的不同调度器scheduler上运行。
•最后,在主线程调度器上调度最后一段蓝色代码(3),以便用新数据更新UI。
即使它看起来很有趣,也很方便,现在也不要为调度器schedulers操心太多。在这本书的后面,你会回到他们那里。
App架构
值得一提的是,Rxswift不会以任何方式改变应用程序的架构;它主要处理事件、异步数据序列和通用通信契约。
你可以使用RX创建实现苹果开发者文档中定义的MVC架构的应用。您也可以选择实现MVP体系结构或MVVM,如果您喜欢的话。
如果您想这样做,RXSwift对于实现您自己的单向数据流体系结构也非常有用。
重要的是要注意,你绝对不必从头开始一个项目,使它成为一个反应式的应用程序;你可以迭代地重构一个现有项目的片段,或者在向你的应用程序附加新功能时简单地使用Rxswift。
微软的MVVM体系结构是专门为在提供数据绑定的平台上创建的事件驱动软件开发的。Rxswift和MVVM确实可以很好地结合在一起,在本书的最后,您将研究这个模式以及如何用Rxswift实现它。
mvvm和rxswift之所以能很好地结合在一起,是因为ViewModel允许您暴露Observable
本书中的所有其他示例都使用MVC体系结构,以使示例代码简单易懂。