WWDC2019 推出了 Combine 框架。这是 RxSwift 的明显竞争对手,RxSwift 实现了反应式流规范。让我们谈谈我们的生活是否发生了变化。
是的,它是 beta 版。设计、性能、API 可能会变得更糟或更好。但很多事情已经很明显了。
我的文章将包含许多可点击的链接(这不是广告),因为事实上一切都已经说过了。剩下的只是将事物按照良好的顺序组合在一起。今天我们将预览:
[TOC]
Backpressure(背压?)
如何处理 Observables 产生元素的速度比观察者消耗它们的速度快的问题?
从历史上看,RxSwift 不支持 backpressure。 Flowable 和 Observable 之间没有像 RxJava 中那样进行分离。这是一个已解决的问题,在可预见的将来不会修改。您可以在此处阅读有关原因的更多信息:
为了解决这个问题,RxSwift 有一组有用的运算符和对重入异常的基本运行时检查,这绝对足以提供舒适的无错误开发。
Combine 支持开箱即用的背压。receive
方法应返回订阅者想要接受的元素数。如果你不在乎,只需返回 Demand.unlimited
即可。
我想这很酷。但我已经编写了很多 RxSwift 代码,并且没有遇到任何需要背压支持的情况。我总是可以使用可用的运算符集来解决它。
类型错误
这是一个非常热门的问题,每次讨论反应式流时都会出现。应该输入错误还是只是一个 Error
? RxSwift 认为 Error
就足够了。你可以在这里阅读人们关于这些内容的最新讨论。
Combine 开箱即用。你必须提供 Publisher
错误类型。也很少有操作符能够处理错误。您可以映射它、替换它、照常处理下一个事件。
老实说,我不知道为什么我们需要一个明确的错误类型。在大多数情况下,我们将错误视为抽象的东西。我们有一些通用的弹出窗口,其中放置了我们最喜欢的 "Something went wrong..."。否则,我们不想处理错误,而是更喜欢显示通常的空屏幕或错误状态。我非常喜欢将错误处理作为单独的流,并且很少使用特定类型。在我们的项目中,没有符合 Swift.Error
的自定义类型。
异常处理
我不知道为什么,但是 Combine 拆分了 throwing/not throwing 运算符。例如,如果你的 map
闭包可能会抛出异常,你应该通过 tryMap
应用它。
这样好吗?我想说,作为一名开发人员,我不在乎。我更喜欢默认抛出闭包的 RxSwift 方式。你不会以某种特定方式使用 try(map|filter|scan)
。你只是不需要那个逻辑。因为,老实说,开发人员不喜欢错误,也不知道如何处理错误。
DisposeBag
DisposeBag
是 RxSwift 内存管理模式。您无需单独保存每个订阅并终止它,只需编写 .dispose(by: disposeBag)
并获取一个负责所有订阅处置的变量即可。当 disposeBag
引用计数变为零时,正常行为是在 deinit
中进行处理。默认情况下您期望这种行为。
然而,这一点以及垃圾收集器的缺失也引入了引用循环的全局问题,没有经验的用户很容易遇到这个问题。当然,有很多方法可以控制它;您甚至可以编写有关正确处理引用循环的测试。我将在下一篇文章中展示更详细的防止内存泄漏的方法,订阅不要错过。
Combine 中没有与 DisposeBag
类似的东西。有一个 Cancellable
(把我带回 Disposable
),你可以取消并释放所有资源。然而,在 ARC 中使用 Cancellable
可能会非常令人困惑。这是一篇很棒的文章,它通过示例展示了 Combine 的问题。
我是 DisposeBag
的忠实粉丝。非常方便,而且非常简单。例如,有一种众所周知的清除单元格中订阅的模式:
swift
final class Cell: UITableViewCell {
private var disposeBag = DisposeBag()
func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
...
}
我在当前项目中最大的单元中有七个订阅。也许我真的不想为七个 Cancellable
中的每一个编写这个清理代码。在我看来,添加一个类似类型的 CancelBag
是合乎逻辑的,但苹果工程师比我更了解他们的东西。因此,只能等待,祈祷。在极端情况下,您可以轻松提供自定义实现。
API
RxSwift vs Combine 不等于 Kotlin coroutines vs RxJava 问题。可以用来处理类似问题的工具和类似的工具并不相同。 API 是完全平等的。是的,文字可能略有不同,但它们是相同的框架,只是以不同的方式编写。如果您对更多细节感兴趣,这里有来自 RxSwift 主要维护者之一、出色的 Shai Mishali 的 RxSwift 和 Combine API 的比较。
是的,没有 flatMapLatest
或常见的特征,如 Maybe
、Completable
或 Driver
。但这只是实施细节。对于苹果来说,它们...... 没有必要。在尝试 SwiftUI + Combine 时,我没有遇到任何因它们缺席而出现的问题。
RxSwift 不会仅仅复制所有的 Combine API。但我会默默地窃取有意义且适用的运算符。例如,这里有一个很酷的 assign
运算符,它已经添加到 RxSwift 中。不要犹豫,RxSwift 使用起来会像 Combine 一样舒适。如果您有兴趣在 RxSwift 中使用您最喜欢的组合运算符,请参与本期。只要投票,我就会为你实现一切。
性能
我想说,RxSwift 和 Combine 一样快。但我不能对你撒谎。这是一个小测试用例,仅应用无意义的运算符。我们通常就是这样编程的,对吗?
结果令人失望。是的,我们很少过滤数百万个元素流,但尽管如此:
遗憾的是我没有额外的 6000 美元购买新的 Mac Pro,所以只有 1000 万个元素。该比率呈线性增长。我不会附上不同数量元素的图表,但您可以复制粘贴此测试并自行运行。
RxSwift 在底层启动许多接收器已经不是什么秘密了。它就是这样设计的,我怀疑有人会轻易找到更好的解决方案。然而,Combine 的设计很长一段时间以来都强调性能。这不仅仅是言语。
嗯...... 很好。显然,来自苹果的开发者没有任何障碍,所有的资源都掌握在手中,可以比社区做得更好。老实说,我不认为它会更好。性能的大幅提升总是好的,但是...... 试着记住您最后一次使用 XCTestCase.measure 是什么时候?我强烈怀疑你的 tableView 滚动不够快只是因为 RxSwift。
Sugar(语法糖?)
你可以同意也可以不同意,但 RxSwift 和 Combine 实际上是相同的框架。Combine 完全在现代 Swift 中实现,因此您可以使用它的协议并提供 Publishers
的自定义实现。但您不会编写自己的 Map
,是吗?因此,我对这一变化不发表任何评论。开放性、扩展性良好。但这种开放性却让开发者拄着拐杖写错了,不尊重契约。该框架应该提供开箱即用的所需一切,并且不允许用户更改规则或重新发明轮子。
行业价值
总会有怀疑者。怀疑论者试图让我们相信电脑不适合简单用户,而 iPhone 则不可能。怀疑论者表示,Swift 只是一个玩具,只是为了吸引新的开发者,苹果将继续用 ObjC 编写。总是有人持怀疑态度,他们说反应式编程是黑魔法,没有什么比古老的委托模式更好的了。
现在社区很兴奋,每个人都喜欢 SwiftUI。但只有少数人考虑了反应式,事实上,反应式是实现应用程序的新方式的核心特征。这取决于你,但我想说这...... 非常重要。
这是 RxSwift 的失败吗?不,这是苹果的胜利。很多人已经注意到,这是长期以来开发者最耀眼的 WWDC。 SwiftUI 绝对很酷。在最新款 iPhone 销量不理想后,苹果需要重新夺回市场地位。他们分析社区请求并尽一切努力哄骗开发人员。他们试图让世界上的每个开发人员都想为苹果编写应用程序。老实说,他们做到了。
就是这样。反应式编程。应该说,RxSwift 在 iOS 开发中并不是很流行。虽然 Android 应用程序没有 RxJava 就无法实现,但我们(iOS 用户)仍然怀疑是否应该尝试这个库。 "这是第三方" 和 "代码太复杂,太神奇了" 成为反对 RxSwift 的流行论点。现在苹果公司宣布了一个反应式框架,意思是 "是的,伙计们。这就是 iOS 开发的未来。现在正式了。处理它" 显然,现在更多的开发人员会将委托更改为 Rx。如果他们不是...... 他们应该是。
iOS 13.0+
较低的 iOS 版本不支持 Combine。你能用它做什么?
- 如果你目前正在使用 RxSwift,那么一两年就不用担心了。我看不出将代码库迁移到 Combine 有任何意义。也许有一天你会觉得使用自定义 RxSwiftCommunity 库没有任何意义,因为所有这些库都可以在官方框架中开箱即用。然后...... 你可以尝试删除 RxSwift。
- 如果您现在想尝试在项目中做出反应,那么最好等待。从 RxSwift 迁移到 Combine 将相当复杂。是的,您可能认为 API 非常相似,但相信我,您会遇到问题。因此,只需在宠物项目中查看它,无需将其添加到您的生产中。
- 如果你现在没有做出反应...... 好吧,正如我已经说过的...... 你绝对应该做出反应。
RxSwift 是目前响应式编程的一个很好的替代方案。您不需要支持 iOS 13.0+ 即可进行响应式编程。我想你可能会看到苹果的新东西。我很惊讶人们会喜欢:"哇,一种设置表格视图的方法,太方便了,太现代了。" 苹果并没有像往常一样发明任何新东西,他们只是让旧的变得更好。对于较低的 iOS 版本,您现在可以用一行配置您的表(我已经使用它两年了):
值得注意的是,反应式编程框架围绕其自身形成了大量方便的扩展。对我来说,反应性已经成为简洁的代名词。您可以在我的上一篇文章中了解 RxSwift 的便利性。
RxSwift 社区真的很棒。因为反应性不仅仅是处理异步事件的一种方法,而且是一种思考、设计应用程序的方法。声明性、单向性、无状态组件...... 您现在就可以使用所有这些功能。类似的东西你必须与 SwiftUI 一起使用。您已经了解 Flutter 中的所有 BLoC 内容。
何去何从
我真的很想说 RxSwift 将永远存在,有一天我会向我的儿子解释它。但是这是错误的。 RxSwift 几年后就会消亡。这不仅仅是炒作的话,而是事实。你可以在这里阅读更多。最特别有趣的是 Krunoslav Zaher 的评论,他让 iOS 开发者的生活变得更加幸福。
你可以随心所欲地解释它,但对我来说,这是一次悲伤的告别。这是一项很棒的工作,我希望我能早点出生,站在这个存储库的起源上。我为此付出的努力太少了,现在正努力追赶。我们努力为对 Combine 感兴趣的用户提供更愉快的 RxSwift 体验。新的类似 Combine 的操作符即将推出。
但这一切都只是对梦想的追求。 RxSwiftCommunity 可以向 UIKit 添加任何扩展。但不幸的是,我们永远无法直接为 Apple 框架做出贡献。Combine 看起来是一个耐用的功能。 Apple 在 RealityKit 中支持它,SwiftUI 在底层使用它。这是合乎逻辑的。 RxSwift 永远只能成为一个让开发变得更容易的第三方。
正确的问题不是 "我应该使用 Combine 吗?",而是 "我到底应该什么时候开始这样做?"。这取决于您的要求。例如,在当前项目中,我的团队仅支持 iOS 12,因此,我们可能很快就会迁移到 SwiftUI + Combine 架构。伙计们,这就是未来,唯一的问题是 "你们多久才能准备好放弃你们的遗产,进入一个勇敢的新世界?"。没有人强迫你,但这是必要的。只是为了防止你的开发人员陷入抑郁。
开源
"为什么他们不开源 Combine?" 成为一个非常受欢迎的问题,这对我来说很奇怪。只是因为所有其他 Apple 框架都不是开源的。他们可能使用普通用户不允许的功能。他们不想解决问题、审查贡献者的 PR。因为他们比其他人能更好地处理这个问题。就是这样,我不想有任何其他理由同意该政策。
Subjective
抱歉,Publisher
是一个非常糟糕的名字。我们有一本由四人组成的书,具有众所周知的可观察模式。我们有伟大的埃里克・迈耶,毕竟,你称其为 Publisher
?当然,我会习惯的,但是...... 在俄语中,Publisher
翻译为 "他",Observable
翻译为 "它"。苹果,多样性怎么样?
此外,RxSwift 支持开箱即用的 dark 模式,这一点我不能说 Joint。你的选择。
我的计划
首先,我要为 RxSwift 的美好生活喝点啤酒。我将进行一些有关 RxSwift 与 Combine 的技术讲座。老实说,我只是想让人们明白他们手中握着的是什么。不仅仅是大声喊出 "华丽"、"惊人" 或 "令人兴奋" 等词语,而是真正理解为什么这是...... 令人兴奋。这不仅仅是一个功能,它是 iOS 开发的新时代。我押注于 Combine 和 SwiftUI,并建议你也这样做。
10 年后,我打算成为一个老人,告诉大家我们是如何使用 RxSwift 的。而且,该死的,太棒了。