首页 iOS.& Swift Books 结合:Swift的异步编程

1
你好,结合! 由Marin Todorov撰写

本书旨在向您介绍组合框架,并为Apple平台迅速撰写声明和反应应用程序。

用苹果自己的话:“结合 Framework提供了应用程序如何处理事件的声明性方法。您可以为给定的事件源创建单个处理链,而不是潜在地实现多个委托回调或完成处理程序。链条的每个部分是一个组合操作员,对从上一步接收的元素执行不同的动作。“

虽然非常准确,而且到目前为止,这一令人愉快的定义可能起初听起来有点太抽象。这就是为什么,在深入研究编码练习并在下面的章节中致力于研究项目之前,您将需要一段时间来学习一些关于组合解决问题的问题以及它用来这样做的工具。

一旦建立了相关的词汇和一些框架的一些了解,您将继续在编码时覆盖基础知识。

逐渐,随着您在书中的进展,您将了解更高级的主题,最终通过几个项目进行工作。

当您在最后一章中涵盖了其他所有其他内容时,请在合并的完整应用程序上工作。

异步编程

以简单的单线程语言,程序按顺序执行。例如,在Pseudocode中:

begin
  var name = "Tom"
  print(name)
  name += " Harding"
  print(name)
end

Synchronous code is easy to understand and makes it especially easy to argue about the state of your data. With a single thread of execution, you can always be sure what the current state of your data is. In the example above, you know that the first print will always print “Tom” and the second will always print “Tom Harding”.

现在,想象一下,您以多线程语言编写了运行异步事件驱动的UI框架的程序,如Swift和Uikit上运行的iOS应用程序。

考虑可能发生的事情:

--- Thread 1 ---
begin
  var name = "Tom"
  print(name)

--- Thread 2 ---
name = "Billy Bob"

--- Thread 1 ---
  name += " Harding"
  print(name)
end

Here, the code sets name‘s value to "Tom" 和 then adds "Harding" to it, just like before. But because another thread could execute at the same time, it’s possible that some other part of your program could run between the two mutations of name 和 set it to another value like "Billy Bob".

当代码同时在不同的核心上运行时,很难说代码的哪个部分将首先修改共享状态。

上面示例中的“线程2”上运行的代码可能是:

  • 在与原始代码的不同CPU核心的同时执行。
  • executing just before name += " Harding", so instead of the original value "Tom", it gets "Billy Bob" instead.

运行此代码时究竟发生了什么,从而依赖于系统加载,并且每次运行该程序时都可能会看到不同的结果。

一旦运行异步并发代码,管理应用程序中的可变状态就会成为已加载的任务。

基金会和uikit / appkit

多年来,Apple一直在改善其平台的异步编程。他们已经创建了几种机制,您可以在不同的系统级别使用,以创建和执行异步代码。您可能在项目中使用了这些,而不会给予他们第二次思想,因为他们是如此基本上写入移动应用程序。

您可能使用以下大部分时间:

  • NotificationCenter:任何时候感兴趣的事件发生了一次代码,例如当用户更改设备的方向或软件键盘在屏幕上显示或隐藏时。
  • 代表模式:允许您定义代表另一个对象代表或协调的对象。例如,在您的应用程序委托中,您定义了新的远程通知到达时会发生的情况,但是当这段代码将被执行或其执行次数时,您不知道。
  • 大中央调度运营:帮助您抽出执行工作的执行。您可以使用它们来安排要在串行队列中顺序执行的代码,或者在具有不同优先级的不同队列中同时运行多个任务。
  • 关闭:创建可以在代码中传递的分离的代码,因此其他对象可以决定是否执行它,多次,以及在什么上下文中。

由于大多数典型代码异步执行一些工作,并且所有UI事件都是异步的,因此无法对哪个订单进行假设是不可能的 整体 您的应用程序代码将被执行。

然而,写入良好的异步程序是可能的。它比......好吧,我们希望它更加复杂。不幸的是,异步代码和资源共享可以产生难以重现,追踪和最终修复的问题。

当然,这些问题的原因之一是实际的,现实的现实应用程序很可能使用所有不同类型的异步API,每个API都有自己的界面,如下所示:

结合旨在向Swift生态系统推出一种新语言,有助于您将更多订单带入异步编程世界的混乱中。

Apple has integrated Combine’s API deep into the Foundation framework, so Timer, NotificationCenter 和 core frameworks like 核心数据 已经说了它的语言。幸运的是,组合也很容易集成到您自己的代码中。

最后,最后但绝对不是最不重要的,Apple设计了他们惊人的新UI框架, Swifui.,与联合一样轻松集成。

为了让您了解Apple如何使用COMMINA的反应编程,这是一个简单的图表,显示了系统层次结构中的组合位置:

各种系统框架,从基础一直到SWIFTUI,依靠组合并提供结合集成作为他们“传统”API的替代品。

Since Combine is an Apple framework, it doesn’t aim to take away the role of well-tested, solid APIs like Timer or NotificationCenter. Those Foundation types are still present and doing their part. Instead, Combine integrates with them and allows all the types in your app that want to talk asynchronously to each other do so via a new, universal language.

因此,如果使用相同的异步工具将应用程序的所有部分连接到网络层和用户界面的数据模型,听起来很有趣 - 你在正确的地方,继续阅读!

结合的基础

声明性,无功编程不是一个新的概念。它已经存在了很长一段时间,但它在过去十年中取得了一个相当明显的卷土。

当Microsoft的一支团队推出一个名为 反应延伸 for .NET (Rx.NET).

微软在2012年制作了RX.NET实现开放来源,从那时起,许多不同的语言已经开始使用其概念。目前,有许多RX标准的端口,如RXJS,RXKOTLIN,RXSCALA,RXPHP等。

对于Apple的平台,已经有几个类似RXSWIFT的第三方反应框架,其实现了RX标准;反应是由rx的启发;星际,这是一种自定义实施和其他人。

组合实现了不同但类似于RX的标准,称为无功流。反应流有一些与RX的关键差异,但它们都同意大部分核心概念。

如果您以前没有使用上述框架中的一个或另一个 - 别担心。到目前为止,对Apple平台的反应编程是一个相当利基的概念,特别是迅速。

然而,在IOS 13 / MacOS Catalina中,Apple通过内置系统框架向其生态系统带来了无功编程支持, 结合.

与来自Apple的任何新技术一样,其应用乍一看略有限制:您只能使用支持支持iOS 13 / MacOS Catalina或更高版本的应用程序。但与Apple投注的任何技术一样,它的支持将迅速变得普遍,并且对组合技能的需求将会激增。

有了这一说,首先学习一些组合的基础知识,看看它是如何帮助你写安全和坚实的异步代码。

结合基础知识

在广泛的中风中,组合中的三个关键移动件是发布者,运营商和订阅者。当然,团队中有更多的玩家,但没有那三个你无法达到太多。

您将在第2章“出版商”中详细了解发布商和订阅者的详细信息&订阅者“和本书的完整第二部分致力于以人类可能的方式熟悉您的运营商。

然而,在这一介绍性章节中,您将获得一个简单的崩溃课程,以为您提供对这些类型的概念,这些类型在代码中以及他们的职责是什么。

出版商

出版商是可以随着时间的推移到一个或多个感兴趣的各方,例如订阅者的类型。无论出版商的内部逻辑如何,哪个都可以是数学计算,网络或处理用户事件,每个发布者都可以发出这三种类型的多个事件:

  1. An output value of the publisher’s generic Output type.
  2. 成功完成。
  3. A completion with an error of the publisher’s Failure type.

Publisher可以发出零个或多个输出值,如果成功或由于失败完成,则不会发出任何其他事件。

Here’s how a publisher emitting Int values could look like visualized on a timeline:

蓝色框表示在时间轴上的给定时间发射的值,数字表示发射值。像你在图的右侧看到的垂直线一样,表示成功的流完成。

三个可能事件的简单合同是普遍的,它可以代表程序中的任何类型的动态数据。这就是为什么你可以使用组合发布者在您的应用程序中解决任何任务 - 无论是关于克里齐奇的数字,使网络呼叫,对用户手势做出反应或在屏幕上显示数据。

而不是始终在您的工具箱中查看正确的工具来抓住手头的任务,而是添加委托或注入完成回调 - 您可以使用发布者。

出版商的最佳功能之一是他们带有错误处理;如果您觉得这样,则错误处理不是您在最终添加的内容。

Publisher protocol is generic over two types, as you might have noticed in the diagram earlier:

  • Publisher.Output is the type of the output values of the publisher. If the publisher is specialized as an Int, it can never emit a String or a Date value.
  • Publisher.Failure is the type of error the publisher can throw if it fails. If the publisher can never fail, you specify that by using a Never failure type.

订阅给定发布者时,您可以知道从中期望的值以及它可能失败的错误。

运营商

运营商 are methods declared on the Publisher protocol that return either the same or a new publisher. That’s very useful because you can call a bunch of operators one after the other, effectively chaining them together.

由于这些调用“运算符”的方法是高度解耦和可编译的,因此可以组合(AHA!)来在执行单个订阅时实现非常复杂的逻辑。

运营商像拼图一样紧密地适合运营商是令人着迷的。如果一个人的输出与下一个输入类型不匹配,它们不能错误地误入错误的顺序或适合:

以明确的确定性方式,您可以与正确的输入/输出类型和内置错误处理一起定义每个异步抽象作品的顺序。这几乎太好了!

作为一个额外的奖金,运营商始终具有输入和输出,通常称为 上游下游 - 这允许他们避免共享状态(我们之前讨论过的核心问题之一)。

操作员专注于与他们从以前的操作员收到的数据一起使用,并将其输出提供给链中的下一个。这意味着没有其他异步运行的代码可以“跳入”并更改您正在处理的数据。

订阅者

最后,您到达订阅链的末尾:每次订阅都以订户结束。订阅者通常使用发出的输出或完成事件进行“某事”。

目前,组合提供了两个内置的订阅者,这使得与简单的数据流合作:

  • 下沉 订阅者允许您使用您的代码提供闭合,这些代码将接收输出值和完成。从那里,您可以使用所接收的活动做任何事情。

  • 分配 订阅者允许您,无需自定义代码, 捆绑 生成的输出到数据模型上的某些属性或在UI控件上通过键路径直接显示数据。

如果您对数据有其他需求,请创建自定义订阅者比创建发布者更容易。 COMMITIME使用一组非常简单的协议,允许您能够在Workshop不为您的任务提供正确的方面构建自己的自定义工具。

订阅

笔记:本书使用该术语 订阅 to describe both Combine’s Subscription protocol and its conforming objects, as well as the complete chain of a publisher, operators and a subscriber.

在订阅结束时添加订阅者,它将在链的开头“激活”发布者。这是一个好奇但重要的细节来记住 - 如果没有订阅者可能会接收输出,发布者不会发出任何值。

订阅是一个精彩的概念,允许您 宣布 具有自己自定义代码和错误处理的异步事件链 只有一次然后,你永远不必再次考虑它。

如果您已全组合,您可以通过订阅描述您的整个应用程序的逻辑,并完成后,只需让系统运行所有内容而无需按住数据或调用此其他对象或其他对象:

一旦订阅代码成功编译并且您的自定义代码中没有逻辑问题 - 您就完成了!按照设计的,每次用户手势等事件都异步“触发”订阅,或者将其出版商唤醒其中一名出版商。

Even better, you don’t need to specifically memory manage a subscription, thanks to a protocol provided by Combine called Cancellable.

Both system-provided subscribers conform to Cancellable, which means that your subscription code (e.g. the whole publisher, operators and subscriber call chain) returns a Cancellable object. Whenever you release that object from memory, it cancels the whole subscription and releases its resources from memory.

例如,这意味着您可以通过将其存储在视图控制器上的属性中,轻松地“绑定”的寿命。这样,当用户从视图堆栈中解雇视图控制器的任何时候,都会将其属性取决于其属性,并取消您的订阅。

Or to automate this process, you can just have an [AnyCancellable] collection property on your type and throw as many subscriptions inside it as you want. They’ll all be automatically canceled and released when the property is released from memory.

如您所见,有很多可以学习,但在详细解释时,它都是逻辑。这正是这个计划是下一个章节的计划 - 从零来慢慢地,以便在本书结束时结合英雄。

结合代码的好处是在“标准”代码上的好处?

You can, by all means, never use Combine and still create the best apps out there. There’s no argument about that. You can also create the best apps without Core Data, URLSession, or even UIKit. But using those frameworks is more convenient, safe and efficient than building those abstractions yourself.

组合(和其他系统框架)旨在在异步代码中添加另一个抽象。系统级别的另一个抽象级别意味着更紧密的集成,这对经过良好的测试和用于持久支持的安全投注技术。

这取决于你来决定组合是否适合您的项目,但这只是你可能没有考虑的一些“专业”理由:

  • 组合集成在系统级别。这意味着组合本身使用不公开可用的语言功能,为您提供您无法自行构建的API。
  • 这 “old” style async code via delegates, IBAction or closures pushes you towards writing custom code for each case of a button or a gesture you need to handle. That’s a lot of custom code to write tests for. Combine abstracts all async operations in your code as “operators”, which are already well tested.
  • When all of your asynchronous pieces of work use the same interface — Publisher — composition and reusability become extremely powerful.
  • 组合的运营商是高度可组织的。如果您需要创建一个新的,那么新的运算符将立即使用其余的组合即插即用。
  • 测试异步代码通常比测试同步代码更复杂。然而,通过组合,已经测试了异步运算符,并且所有留下的所有这些都是测试您的业务逻辑 - 即,如果订阅输出预期结果,则提供一些输入和测试。

如您所见,大多数好处都围绕着安全和便利。结合框架来自苹果的事实,投资编写合并代码看起来很有希望。

应用程序架构

由于这个问题很可能在头脑中发出警报,请查看合并如何更改您的预先存在的代码和应用架构。

组合不是一个影响您如何构建应用程序的框架。组合处理异步数据事件和统一通信合同 - 例如,您不会如何将项目中分开责任的方式。

您可以在MVC(Model-View-Controller)应用程序中使用组合,您可以在MVVM(Model-View-ViewModel)代码中使用它,viper等。

这是采用很重要的组合的关键方面之一,即早期了解很重要 - 您可以迭代地和选择性地添加组合代码,仅在您希望在Codebase中改进的零件中使用它。这不是你需要做的“全部或全无”选择。

您可以通过转换数据模型来转换您的数据模型,或适应网络层,或者只需在您添加到应用程序的新代码中,只需使用组合即可,同时保持现有功能。

如果您正在同时采用组合和Swifui,这是一个略有不同的故事。在这种情况下,从MVC架构中删除C确实有意义。但是,这就是使用合并和SWIFTUI在串联中 - 在同一个房间时,这两个人只是在火中。

视图控制器只是没有任何机会反对联合/ SWIFTUI团队。当您将VARTIVE编程一路从数据模型到您的视图时,您不需要使用特殊控制器来控制您的视图:

如果这听起来很有趣,你就是为了一种待遇,因为这本书包括使用这两个框架在第15章中使用这两个框架的坚实介绍,“在实践中:SWIFTUI& Combine.”

预订项目

在本书中,您将首先从概念开始并继续学习并尝试众多运营商。

与其他系统框架不同,您可以在游乐场的孤立背景下相结合成功工作。

在Xcode Playground中学习使得在通过给定章节的进步并在Xcode的控制台中立即查看结果,可以轻松地向前移动和快速实验:

组合不需要任何第三方依赖项,因此通常,每个章节的初学游乐场代码中包含的一些简单辅助文件就足够了,以便您运行。如果Xcode在您在游乐场进行实验时陷入困境,则可能会解决问题。

一旦移动到更复杂的概念而不是与单个运营商一起使用,您将在游乐场和真正的Xcode项目之间交替,如黑客新闻应用程序,这是一个实时显示新闻的新闻记者:

重要的是,对于每一章,您从提供的初学游乐场或项目开始,因为它们可能包括一些与学习结合无关的定制辅助代码。这些Tidbits是预先写的,所以你不会分散自己的注意力。

在最后一章中,您将使用在整个书中学习的所有技能,因为您完成开发依赖于组合和核心数据的完整IOS应用程序。这将为您提供最终推动您的道路,以建立使用相结合的现实应用!

关键点

  • 结合是一种声明性的反应框架,用于随着时间的推移处理异步事件。
  • 它旨在解决现有问题,如异步编程的统一工具,处理可变状态并使错误处理起始团队播放器。
  • 组合围绕三种主要类型: 出版商 随着时间的推移发出事件, 运营商 异步处理和操作上游事件和 订阅者 消耗结果并对他们做一些有用的东西。

然后去哪儿?

希望这一介绍性章节一直有用,并给你初步了解问题,结合地址以及查看它所提供的一些工具,使您的异步代码更安全,更可靠。

从本章中的另一个重要外卖是从组合的期望以及超出其范围的内容。现在,当我们随着时间的推移,我们知道当我们谈论反应代码或异步事件时,您就知道了。当然,当然,您不希望使用合并来神奇地解决您的应用程序在导航或屏幕上绘图的问题。

最后,在即将举行的章节中味道在即将举行的章节中享受了什么,希望您对Swift的混合和反应性规进行兴奋。向上和向前,我们走了!

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC