利用RxSwift和RxBluetoothKit的功能以无缝方式与HR监视器交互。 集成核心蓝牙以连接到心率监测器需要进行大量工作。 如果您熟悉反应式编程,则RxBluetoothKit使事情变得容易得多。 但是您仍然需要了解Core Bluetooth的工作原理,并编写大量代码以与HR监视器对话。 RxHeartRateMonitors是RxBluetoothKit和Core Bluetooth之上的一层。 我构建了它以无缝,反应式的方式与心率监测器进行交互。 您可以下载该项目并进行尝试。 它有什么作用? 仅列出心率监测器。 连接和断开监视器。 从监视器读取心率。 记住以前连接的显示器。 自动连接到以前连接的显示器。 读取蓝牙状态。 读取监视器状态。 在本文中,我将解释有关该项目的一些细节。 为什么创建它以及一些实现细节。 我正在作为一个团队的一部分来构建健身应用程序。 该应用程序的要求之一是列出可以使用的心率监测器。 同样,用户应该能够连接并从此类设备读取值。 我以为CoreBluetooth将使这项工作变得容易。 事实并非如此。 核心蓝牙太通用 它抽象了BTLE协议栈,提供了与各种设备进行交互的方式。 发现外围设备非常容易。 但是随后您还需要发现其服务,特征和代码。 最后,您需要读取原始数据并将其转换为正确的格式。 代码可能变得非常复杂。 为了给您一个想法,以下是从监视器读取心率值所需的步骤: 启动中央管理器。 发现外围设备( 需要一名代表 ) 过滤提供心率监测服务的外围设备 连接到外围设备( 需要委托 ) 发现该外围设备的服务( 需要委托 ) 发现服务的特征( 需要委托 ) 找到代表心率的特征 订阅特征值( 需要委托 ) 将原始数据转换为正确的格式。 实现必需的委托需要太多代码。 如果您看一下本教程或本文,则可以了解其复杂性。 苹果公司也有一个例子(在Objective-C中)。 复杂性很高,因为Core Bluetooth是通用的。 […]
注意:本文假设您了解RxSwift的基础知识或对MVVM有了一般的了解。 如果您不这样做,Internet上有大量令人难以置信的资源(包括下面链接的资源)可以帮助您入门。 另一个注意事项: Point-Free的 Brandon Williams 和 Stephen Celis 最初对这个想法睁开了眼睛 。 如果您尚未签出,则应该这样做。 他们发布的内容对于Swift开发人员来说绝对是必不可少的,订阅可能是您今年做出的最有价值的投资。 介绍 关于如何将RxSwift与MVVM配对,已经写了成千上万的单词,并进行了数小时的讨论。 在Grailed,我们一直热衷于与社区进行创新,以改善我们的代码并为我们的消费者创造更好,更可靠的产品。 出于这个目标,我们一直在使用一种MVVM形式,该形式通过函数式编程和RxSwift来提供可靠性,可测试性和稳定性。 我们喜欢它的许多方面,但是遇到了大多数MVVM开发人员都会熟悉的一系列问题。 问题所在 有时可能不清楚如何组织代码。 在互联网上漂浮的数十种MVVM变体中,每个视图似乎对视图层如何与其视图模型进行交互都有不同的看法。 缺少清晰的模式可能会使MVVM中的开发感到特别和不一致,从而可能导致可维护性问题。 视图模型也可能变得非常冗长。 由于许多版本的MVVM中缺乏结构,其他人选择为视图模型编写更为明确的输入和输出合同,但传统上这是以大量的样板为代价的。 同时,其他版本也不能防止视图模型的使用者错误地使用其API。 从视图层订阅视图模型的输入众所周知是一种反模式,但这是我在多个生产代码库中看到的。 理想情况下,编译器将阻止我们完全犯此错误。 由于Swift的类和结构初始化规则,视图模型也可能变得难以设置。 例如,当您必须将输出Observable设置为类或结构的属性时,这些输出取决于输入Subject 。 有时您会遇到这样的情况:在完成所有属性的初始化之前,您不能引用self ,但是由于需要引用self ,因此无法初始化属性。 忘记绑定视图模型的输入或输出也很容易,而且编译器在做错事情时也不会帮助我们找出问题。 编译器是我们的朋友,如果它可以帮助我们,那就太好了,这样我们就不会犯这种愚蠢的错误。 一种解决方案 现在,我们已经解决了RxSwift带来的MVVM的一些痛点,让我们以一个流行的MVVM风格编写一个简单的代码示例,并研究如何改进它。 所有笔触的开发人员都知道,编写纯函数可以解锁可测试性和可理解性的级别,否则,即使不是不可能,也很难实现。 问题在于我们编写的许多类型的代码无法完全适合纯函数,因此我们努力为尽可能多的模型建模纯函数。 这种见解驱使许多MVVM开发人员创建他们的视图模型,以具有一组明确的输入和输出,以便我们可以更类似于功能地对待它们。 输入是函数调用或Subject ,输出是回调,可变变量或Observable ,并且视图的业务逻辑在视图模型内部建模为输入到输出的转换,主要是在视图模型的初始化程序中。 Kickstarter开源应用可能是第一个,也是最知名的迭代。 尽管它是用ReactiveSwift编写的,但它们的想法几乎是相同的。 这个开源的应用程序使许多开发人员(包括我自己)对我们如何使用MVVM实现应用程序的可测试性和稳定性打开了眼睛。 让我们以经典的RxSwift为例,使用用户名和密码字段以及登录按钮的简单登录表单。 我们只希望在用户名和密码均填写后才启用该按钮,并且当用户单击该按钮时,我们希望显示一些有关其成功登录的消息(我们将在此处对其进行硬编码,在以后的文章中将详细介绍如何处理网络)。 这是一个示例,说明如果遵循Kickstarter应用程序中列出的模式,该如何编写。 如此小的屏幕在这里有很多事情要做,所以花一点时间来消化它。 我非常喜欢这种风格的几件事: 视图模型创建了一个非常明确的契约,关于它的功能以及应如何使用它 很少有错误使用视图模型API的方法 视图模型没有副作用 从外部查看此视图模型并了解如何对其进行测试非常容易 […]
错误处理真的很难 ,可能重新读取一个文件可能出错,重新读取成功后,修改内容再去保存可能出错。各种的错误都可能发生,使开发变得很难。 Objective-C的错误处理 写过Objective-C的朋友们都知道,在Objective-C中处理错误非常麻烦: NSError *错误; id json = [NSJSONSerialization JSONObjectWithData:data,options:0,error:&error]; 如果(json){ //解析成功,继续处理 }其他{ //出现错误,处理错误 } 最重要的逻辑是处理解析JSON成功的事情,但考虑到可能出现错误,不得不写很多的代码处理错误。经常可能这种错误处理的代码会写的很多,难以想到起初的逻辑是什么,为此更多时候都会选择忽略错误,直接在error参数中初始化一个nil,从而简化代码的逻辑。 Swift的错误处理 Swift在处理错误时,采用了do catch的方案。 当然Objective-C也有类似的方案,只是根本就没有人用。 在定义一个可能出现错误的方法中加入一个throws的标记就可以了。 公共类func JSONObjectWithData(data:NSData,选项opt:NSJSONReadingOptions)抛出-> AnyObject 处理错误的时候代码会写成这个样子: 做{ 让json =试试NSJSONSerialization.JSONObjectWithData(数据,选项:NSJSONReadingOptions.AllowFragments) 打印(json) } { 打印(错误) } 参见Objective-C,最强大的地方就是可以在这里调用多个可能引发错误的方法。 做{ 让数据=尝试NSData(contentsOfFile:路径,选项:NSDataReadingOptions()) 让json =试试NSJSONSerialization.JSONObjectWithData(数据,选项:NSJSONReadingOptions.AllowFragments) 打印(json) } { 打印(错误) } 从一个文件中读取数据(NSData),再将其解析成JSON,这两个步骤都可能出现错误,幸运的是可以将这些错误统一起来处理,上面的两个尝试中错误,都将走到catch的闭包中。 然而异步的错误处理呢? 处理异步下的错误情况 仍然是Alamofire的例子: request(.GET,“ https://github.com/fluidicon.png”) .responseData {(回应) 切换response.result { 案例。成功(让数据): […]
我很高兴地宣布,在第10周内,我接受了加入团队的邀请,成为初级iOS开发人员☺️! (*我原本打算在2018年8月/ 9月发布这篇文章,但生活受阻:D。我现在是Linkedin的REACH软件工程师学徒,期待有关此的近期文章) 这是我在10到12周内学到的一些东西: 错误烤面包机 您可能想知道,什么是错误烤面包机? 这是一个自定义视图,当触发指定的错误时,该视图将从屏幕底部向上滑动。 错误烤面包机向用户显示一条消息,然后烤面包机向下滑动并消失。 之所以称其为烤面包机,是因为其行为与从烤面包机中弹出的烤面包非常相似! 在此示例中,由于必须选择三明治样式才能前进到下一个屏幕,所以会触发错误烤面包机。 这被制成自定义UIView,因为它在几个屏幕上使用。 错误烤面包机的动画也封装在UIView中。 可以重复使用此UIView,而不必在需要此错误烤面包机的所有屏幕上重复相同的代码。 选项集 这里发生了很多事情。 这是自定义formfieldViews(可重用的自定义UIView)的stackview。 如果输入有效,则会出现一个选中标记。 如果输入无效,则出现X,以使用户知道输入无效。 如果用户点击一个表单域,则其下划线将为深色。 用户导航出该表单域后,下划线颜色就会消失。 如果输入无效并且用户导航离开,则下划线变为红色表示输入无效。 选项集用于表示表单域的状态。 选项集很有趣,因为可以将这些值组合起来以表示状态。 例如,如果字段输入有效(原始值4)并且用户当前正在与文本字段(聚焦状态)(原始值2)进行交互,则有效且聚焦的文本字段的总原始值为6。 国家必须被赋予价值。 状态可以存储在数组中 在updateStyle中,它的调用函数可更新和设置bordercolor和图像的动画 一个示例是处于聚焦状态(当用户与表单域进行交互时)(使用RxSwift): 使用三元运算符-如果正在编辑文本字段,则状态为焦点,否则状态为焦点 这就是本系列的全部内容! 谢谢您阅读🙂 在下面通过👏表示赞扬您的支持,在Twitter上关注我的iOS开发者之旅,并在Twitter上与我联系
本文使用RxSwift来说明其观点。 如果您不熟悉该库,则可以阅读其 GitHub存储库上的简介 。 非常感谢 Hugo Saynac 的表现,使我意识到了这个问题,并提出了我将在下面描述的第二种解决方案。 与许多语言一样,Swift允许开发人员引用实例方法,并在以后使用它引用或调用此方法。 尽管语法使它很容易利用,但其底层实现可能会导致创建一些棘手的保留周期。 在本文中,我将解释这种保留周期何时产生,是什么原因造成的,最后是避免它们的难易程度。 方法参考如何工作? 让我们考虑以下类: 要了解真正的原因,我们需要深入研究方法引用的工作方式。 这是什么意思 ? 我们需要记住, printInput是一个实例方法,因此它的引用需要知道在调用该方法时self将引用哪个值。 即使像我们的示例中那样,该方法实际上并没有使用self ,这也是正确的,并且这是很有意义的,因为子类可能会很好地覆盖该方法并在其新实现中使用self 。 这就是WillNeverBeFreed.printInput为我们提供方法引用的原因,该方法的self值尚未绑定。 它采用函数的形式,该函数以self的值作为参数,并返回对实际实例方法的引用。 因此,对该方法的绑定引用存储了一个强指针,该指针将用于self 。 当我们调用.subscribe(onNext: printInput) ,我们将绑定到self的方法的引用作为参数传递给了参数,这导致了保留周期。 两种可能的解决方案 为了解决这个问题,我们基本上需要找到一种方法的参考方法,同时又不能以很强的方式捕捉self 。 进行的第一种方法是定义一个像这样的函数: 它所做的是在一个弱变量中捕获self的值,直到实际调用该方法为止,如果仍然为self分配了值,它将在该点调用它,否则它将不执行任何操作。 然后我们像这样使用它: 另一种合理的方法是将方法声明为计算属性返回的闭包: (在这种情况下,我们在闭包中不使用self,因此不会捕获它。但是,如果使用并捕获了[weak self] ,则将指示[weak self]以避免捕获强引用) 然后,使用它的语法恰好是我们要实现的语法: 一切都是权衡 上述两种解决方案都设法解决了保留周期的问题,但是每种解决方案都有其特定的缺点。 使用第一个解决方案将使语法稍微简洁一些,但是它具有使用实际方法的优势,既可以覆盖它们又可以调用其超级实现。 第二种解决方案保留了更简洁的语法,但是是以使用闭包而不是方法为代价的,这是Swift 3所付出的代价:闭包可以在子类中覆盖,但是覆盖的闭包需要在使用之间进行选择[weak self]并调用超级实现。
这项工作的灵感来自@andrestaltz所缺少的《反应式编程简介》。 我针对那些由于缺乏良好参考而努力学习RxSwift的人,在RxSwift中重新创建了他的RxJS示例代码,并进行了逐步演练(就像我所做的那样)。 因此,您发现自己在学习这种新的Swift趋势时遇到了麻烦吗? 你不是一个人。 RxSwift很难,尤其是缺乏良好的参考。 每个教程要么太笼统,要么太具体,而ReactiveX文档只是无济于事: Rx.Observable.prototype.flatMapLatest(selector,[thisArg]) 通过合并元素的索引,将可观察序列的每个元素投影到新的可观察序列序列中,然后将可观察序列的可观察序列转换为仅从最新可观察序列产生值的可观察序列。 我最终研究了RxSwift示例和一些开源应用程序。 RxSwift的第一个文档引入了RxSwift的Binding或Retry,这些东西我一无所知。 另外,阅读代码也不容易,因为它同时与RxDataSources和Moya / RxSwift一起详细介绍了RxSwift。 因此,我决定编写一个示例应用程序,该示例应用程序将提供精确的“谁要遵循”模式以及逐步说明。 这等效于Andre的工作,但改用Swift编写,我希望这可以帮助您比我更轻松地学习RxSwift。 什么是反应式编程? 轻击按钮,在文本字段内键入一个字符等,由用户触发的每次出现都可以视为典型的异步事件。 如果我们的用户反复点按某个元素,或者连续在搜索栏中键入该怎么办? 这次我们有了异步事件流 。 –a — bc — d — X — |-> a,b,c,d是事件 X是错误事件 | 是“完成”信号 —>是时间表 您可以从任何内容中创建数据流,而不仅仅是点击或键入事件。 流便宜且无处不在。 任何事物都可以是一个流:变量,用户输入,属性,缓存,数据结构等。例如,假设您的Twitter feed将是与点击事件相同的数据流。 您可以收听该流并做出相应的反应。 最重要的是,您将获得令人惊叹的功能工具箱,以组合,创建和过滤任何这些流。 这就是“功能”魔力的所在。流可以用作另一流的输入。 甚至多个流都可以用作另一个流的输入。 您可以合并两个流。 您可以过滤流以获得另一个只包含您感兴趣的事件的流。您可以将数据值从一个流映射到另一个新流。 buttonTapStream:— t —- t–t —- t —— t-> vvvv map(t变为1)vvvv — […]
当您查看代码并几乎不了解它应该做什么或它需要这么多行时,每个开发人员都会意识到这一点。 这并不意味着您必须完全从头开始,但是对重构代码的一部分开放很重要。 重构的目的是使您的代码更易于理解,测试和/或更新。 这是许多文章的第一篇,我将分享我的应用程序We To Too的重构过程,以及沿途学习的模式,库和工具。 四年前,当我第一次开始学习iOS开发时,我学到的设计模式是MVC,它代表Model View Controller。 下图以MVC模式显示了用户交互: 如果您正在学习iOS并依赖Apple的文档,您会注意到它们遵循MVC模式。 随着时间的推移,随着应用程序的增长,很容易将应用程序的所有逻辑写入视图控制器,从而导致难以测试和理解的超大型类(通常称为Massive View Controller)。 MVVM是最近流行的另一种设计模式,它代表Model-View-ViewModel。 视图模型允许业务逻辑与视图控制器分离。 这样,我们的视图控制器就变得很小,只专注于UI。 视图模型通常不导入UIKit,并且不了解视图或视图控制器。 这使视图模型可以进行单元测试。 该模型通常是表示数据的结构或简单类。 遵循MVVM模式并不意味着您的代码将是完美的。 正如您可以拥有大量的视图控制器一样,您也可以拥有大量的视图模型。 您不必在整个应用程序中使用一种设计模式,这取决于哪种功能最适合您要实现的功能。 在上面的MVVM图中,有诸如“数据和用户操作绑定”,“更新”和“通知”之类的术语。 您如何实际在MVVM中实现这些关系? 许多开发人员通过反应式编程为这些关系建模。 响应式编程是软件开发中已经存在了数十年的范例,它使开发人员可以编写专注于异步数据流的声明性代码。 查看Andre Staltz撰写的这份出色的文档,其中有更多解释。 响应式扩展(Rx)是响应式编程如何特别是在移动开发中普及的方式。 为什么我要学习Rx? 用户期望现代应用程序能够响应。 将所有内容都视为带有输入和输出的流,可以确保始终对发生的任何更改(无论是用户操作还是网络请求)做出反应。 响应式编程使用观察者设计模式,订户“侦听”流(可观察)。 有关反应式编程和RxSwift的更多信息,请观看Shai Mishali的精彩演讲或阅读文档。 我之前写过关于可测试视图模型的信息,但是我们将如何测试它们呢? 我决定使用John Shore创建的Red green重构方法。 这样可以确保在编写代码时遵循TDD思维方式。 在逻辑完成之前编写测试, 然后编写逻辑以符合测试要求。 测试通过后,您将找到无需破坏测试即可改进代码的方法。 Kickstarter在其代码库中遵循此方法,您可以观看此视频以了解其工作原理。 他们使用ReactiveSwift并编写了自己的测试助手。 我正在使用RxSwift,发现了一个非常有用的Cocoapods库,称为RxExpect。 现在,我们已经为理解MVVM和反应式编程的基础奠定了基础,让我们开始重构我的应用程序We Read Too的重构,该应用程序全部用Swift编写。 我们也阅读是一款具有以下功能的图书目录应用程序: 集合视图显示按年龄类别过滤的书籍 按作者,标题或描述搜索 详细信息视图,显示书的详细信息以及从Goodreads提取的数据,并允许用户共享书或在Safari / […]
如今Rx蓬勃发展,我认为如果每个开发人员都使用RxSwift会更好。 Rx =反应式编程,这是一个与数据流和变化的传播有关的声明式编程范例,例如,在命令式编程中,将a = b+c设置a = b+c将a的结果赋给a b+c ,然后是b的值 和c 可以更改而不会影响a的值,但是在反应式编程中,a的值 只要b的值自动更新 或c 更改,而程序不必重新执行语句a:=b+c 确定a的当前赋值。 维基百科 ReactiveX是来自以下方面的最佳创意的组合 观察者模式,迭代器模式和功能编程。 ReactiveX不仅仅是一个API,它是编程的想法和突破。 它启发了其他几种API,框架,甚至是编程语言。 在ReactiveX中,观察者订阅了一个Observable。 然后,该观察者对可观察对象发出的任何项目或项目序列做出反应。 这种模式有助于并发操作,因为它在等待Observable发出对象时不需要阻塞,而是以观察者的形式创建了一个哨兵,随时准备在Observable以后的任何时间做出适当的反应。 在ReactiveX中,许多指令可能会并行执行,并且随后会捕获其结果,您以“可观察”的形式定义了一种用于检索和转换数据的机制,然后为它订阅了一个观察者,在此之前,当观察员站岗时,定义好的机制便开始行动起来,以随时捕获并响应其排放。 这种方法的优点是,当您有一堆彼此不依赖的任务时,可以同时启动所有任务,而不必等每个任务都完成才开始下一个任务-这样,您的整个完成任务捆绑包所需的时间与捆绑包中最长的任务一样长。 热点观察 在创建项目后立即发出项目,因此以后订阅该Observable的任何观察者都可以开始观察中间位置的序列。 冷观测 等到观察者订阅它之后才开始发射项目,因此可以保证这样的观察者从一开始就可以看到整个序列。 RxSwift功能强大且功能强大,可节省大量开发人员的生命和精力,可用于: 1-绑定 Observable.combineLatest(firstName.rx.text,lastName.rx.text){$ 0 +“” + $ 1} .map {“问候语,\($ 0)”} .bind(发送至:greetingLabel.rx.text) 2-代表 而不是进行乏味且无表情的操作: 公共功能scrollViewDidScroll(scrollView:UIScrollView){[弱自我] self?.leftPositionConstraint.constant = scrollView.contentOffset.x } …写 self.resultsTableView .rx.contentOffset .map {$ 0.x} .bind(发送至:self.leftPositionConstraint.rx.constant) […]
有时您会知道给定的操作将花费一些时间,它可能是网络呼叫(尤其是在移动网络中,网络状况很差),处理繁重的图像,甚至是通过Core Location获取用户的位置。 在那种情况下,大多数时候,需要在屏幕上的某个位置显示加载程序。 设计如何在命令式编程世界中显示加载程序很容易: 但是,如果我们尝试遵循反应式编程的理念,情况可能会有所不同: 您将如何创建一个Observable ,它仅在使用getMe()函数时在网络请求进行时发出true ,而在网络请求完成时发出false ? Dø方法 第一种幼稚的方法是使用.do()运算符,然后侵入自己的方式来构建所需的Observable流。 由于它正在修改外部资源的状态(此处为rx_isLoading ),因此.do()运算符具有副作用。 它工作得很好,但是摆脱副作用并继续使用纯流会更好吗? 是的,它将! 最近,我考虑过如何避免Dø方法带来的任何副作用。 我最终写了一个扩展程序,该扩展程序将monitorLoading()函数添加到任何ObservableType 。 有了它,开发人员可以通过调用以下命令来计算3种不同的流: .data()返回返回操作最终结果的流。 .loading()以返回发出Bool的流,以了解操作是否加载。 .error()返回操作失败时发出Error的流。 结果是优雅的,并且易于使用: 从那里,它很容易知道请求何时加载,使您能够根据需要显示或隐藏加载器: 另外,该扩展提供了一个API,以防网络调用失败并出现错误。 通过对.monitorLoading()的结果调用.error()可以访问它。 我不会花时间在.monitorLoading()扩展的实现细节上,但是这里是结果,可以随时使用和改进! 谢谢阅读! 如果您喜欢阅读的内容,请随时分享或发表评论。 您也可以在Twitter上关注我。
我很幸运能够参加尝试! Swift Swift纽约今年,我观看了许多有趣的演讲。 我最喜欢的是Paul Fenwick对机器伦理的预测,以及Carl Brown对Swift中常见错误的有趣分析。 我强烈建议您关注这些家伙,听听他们的意见。 但是,确实发生了两件事情,这使我对新思想产生了开放的想法,从而导致了我日常工作中急需的改变。 第一个是Ash Furrow主持的RxSwift研讨会,第二个是Nataliya Patsovska的MVVM演讲。 本文假定您具有响应式和函数式编程的一些基本知识,但希望所有开发人员都可以一定程度地访问它们。 每天,我在Match的BLK应用程序上工作,在该应用程序中,我使用RxSwift的几率极低。 直到最近,我的使用仍主要限于网络代码和模型转换。 我知道我没有充分利用框架的潜力,但是由于某种原因,我仍然故意不了解一些我现在认为使其真正强大的核心功能。 Ash帮助我发现了其中一些功能,现在我感到有能力以更具反应性的风格编写代码。 但是,在查看新代码之前,我认为查看一些旧代码对理解我的问题很重要: 这是与我的应用程序中其他序列相似的Observable序列的示例。 我几乎所有的RxSwift代码都看起来像这样。 在这里,我要从IBAction函数调用fetchUsers() ,在其中创建一个新的Observable(API请求),转换所得数据,然后在序列上调用订阅 。 为了简洁起见,此代码示例和后续代码示例中已省略一些代码。 我将其留给读者来推断(或简单地忽略)此链中某些函数的实现细节,例如mapModel(model:) 。 您可能还注意到此功能包含副作用。 具体来说,我正在显示/隐藏自定义加载视图,并重新加载表视图。 最后,我有两个“配置”和处理错误情况的功能。 这是声明式外观背后的命令式编程。 我对RxSwift还是很陌生,但是现在我已经学会了将这类订阅视为一种代码味道。 让我们从列举此代码的潜在问题开始: 函数fetchUsers()势在必行。 每次要获取用户时,IBAction必须调用此函数。 如果将按钮轻击表示为事件的可观察流,那么将此流绑定到获取程序会更好吗? 可观察对象具有一个内置函数,用于处理称为do的副作用。 我们可以使用此功能来显示/隐藏加载视图,但是如果此Observable序列根本不关心UI,会更好吗? 在很多情况下,在Observable上调用订阅是很有意义的,但这可能不是其中一种。 此序列的最终目的不是执行某些副作用,而是获得一个具体的模型:一组用户。 将此Observable 绑定到需要数据的Observer有意义吗?