排列组合从零开始
Often in life when we are presented with something new to us, that we don’t fully understand, we tend to treat it as a threat. It’s only after spending some time with it, making sense of what it means to us, that we start to feel comfortable around it.
在生活中,当我们经常遇到一些我们不完全了解的新事物时,往往会将其视为威胁。 只是花了一些时间,对它意味着什么对我们意味着什么,我们才开始对它感到自在。
In the IT world, this is no different. Every now and then we are presented with a new tool that creates a big fuzz in the community and everyone starts talking about it. The new cool kid in town. Nonetheless, this new cool kid, instead of encouraging us to get closer, often repels us.
在IT世界中,这没有什么不同。 时不时地为我们提供一个新工具,该工具会在社区中引起很大的反响,每个人都开始谈论它。 小镇上的新酷子。 但是,这个新酷的孩子并没有鼓励我们靠近,反而常常排斥我们。
The new cool kid I’m talking about today arrived a year ago (2019) with the name of Combine.
我今天谈论的一个很酷的新孩子是一年前(2019)到来的,名字叫Combine。
If you are like me and you didn’t have any experience with a reactive programming framework before, then you probably felt a bit like I described above.
如果您像我一样,并且之前没有使用React式编程框架的经验,那么您可能会感到有点像我上面所述。
That’s why I decided to write this series because now that I finally got to know it I can see the amazing potential it has, and I want to try to help you pass that initial intimidating phase so you can assess for yourself if you want to make it part of your programming life or not.
这就是为什么我决定写这个系列的原因,因为现在我终于知道了它,我可以看到它的惊人潜力,并且我想尽力帮助您度过最初的令人恐惧的阶段,以便您可以自己评估自己是否愿意它是否会影响您的编程生活。
So, allow me to explain shortly how this series is structured so you can jump to the parts that interest you the most.
因此,让我简短地解释一下本系列的结构,以便您可以跳到最感兴趣的部分。
First, I will go over a few relevant concepts that will help you understand what Combine is. 首先,我将介绍一些相关概念,这些概念将帮助您了解什么是Combine。 Then, I will present to you a more hands-on explanation of its different members and how to use them. 然后,我将向您提供有关其不同成员以及如何使用它们的更多动手说明。 And last but not least, combine all of it (😜) in a simple app so you can see how everything fits together. 最后但并非最不重要的一点是,将所有内容(😜)组合到一个简单的应用程序中,以便您可以看到所有内容如何组合在一起。If we look at Apple’s documentation about Combine, the first sentence we will read is:
如果我们查看Apple的关于Combine的文档,我们将看到的第一句话是:
“The Combine framework provides a declarative Swift API for processing values over time…”
“ Combin框架提供了声明性的Swift API,用于随时间处理值……”
From this definition, two concepts that caught my attention. The first is “declarative API” and the second “processing values over time”. So let’s see what they mean before going further into the framework.
从这个定义来看,两个概念引起了我的注意。 第一个是“声明性API” ,第二个是“随时间推移处理值”。 因此,在进一步研究框架之前,让我们看看它们的含义。
When you search for declarative programming on Google you will most definitely stump upon many different concepts and definitions, and yes, it can get confusing. However, the discussion is centered around Imperative vs Declarative, so to put it in simple terms:
当您在Google上搜索声明式编程时,无疑会遇到很多不同的概念和定义,是的,它可能会造成混淆。 但是,讨论主要围绕命令式与声明式,因此简单来说:
“Imperative programming is how you do something, and declarative programming is describing what you want to achieve.”
“命令式编程是怎么搞的东西,和声明式编程是描述你想要达到的目标。”
An analogy in the English language for this could be:
用英语进行的比喻可能是:
Imperative: I’m going to walk to the burger joint at the corner and get myself a cheeseburger.
当务之急:我要走到拐角处的汉堡店,给自己一个芝士汉堡。
Declarative: I want a burger.
声明式:我要一个汉堡。
In the first, you are specifically saying how you are going to get that burger you are craving, while in the second, you are merely saying what you want.
在第一个中,您专门说出如何获得自己渴望的汉堡,而在第二个中,您只是说您想要什么。
Moving into a more technical example.
进入一个更具技术性的例子。
func filterOddNumbers(from array: [Int]) -> [Int] { var result: [Int] = [] array.forEach { (number) in guard number % 2 != 0 else { return } result.append(number) } return result } func filterOddNumbers(from array: [Int]) -> [Int] { return array.filter({ $0 % 2 != 0 }) }It might not be obvious, but the main difference between these two examples is that in the first we are specifically saying how we are filtering out the integers, while in the second we only specify what we want to achieve, without going into the details of how to do so. Even though you are providing the predicate to which compare the results, you don’t know how it will be used.
可能并不明显,但是这两个示例之间的主要区别在于,在第一个示例中,我们专门说明了如何过滤整数,而在第二个示例中,我们仅指定了要实现的目标,而没有涉及到怎么做。 即使您提供与结果进行比较的谓词,您也不知道将如何使用它。
This is the heart of declarative programming; it allows you to abstract your code from the context on which is running to give you the possibility to use it in many different scenarios.
这是声明式编程的核心。 它允许您从运行的上下文中抽象代码,从而使您有可能在许多不同的场景中使用它。
It’s important to mention:
重要的是要提到:
“Many declarative approaches have some sort of underlying imperative abstraction.”
“许多声明性方法都有某种基本的命令式抽象。”
If you are interested in diving deeper into the subject be sure to check out these references:
如果您有兴趣深入研究该主题,请务必查看以下参考资料:
Now, when we look at the second concept, processing values over time, we could relate it to the definition of Reactive Programming (RP)
现在,当我们看第二个概念(随时间推移处理值)时,我们可以将其与React式编程(RP)的定义相关联
“Declarative programming paradigm concerned with sequences of data elements made available over time (data streams) and the propagation of change.”
“声明性编程范例涉及随时间推移可用的数据元素序列(数据流)和变化的传播。”
In simple terms, Reactive programming is the practice of programming with asynchronous data streams or event streams.
简而言之,响应式编程是使用异步数据流或事件流进行编程的实践。
It has many similarities with the Observer Pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them of any state change. However, one of the benefits of RP is that it allows a much higher granularity than the observer pattern.
它与观察者模式有很多相似之处,在观察者模式中,一个称为主题的对象维护着一个其依赖者的列表,称为观察者,并将状态变化通知他们。 但是,RP的好处之一是它允许比观察者模式更高的粒度。
For now, this should be enough, once we start discussing Combine it will make more sense.
目前,这已经足够了,一旦我们开始讨论“合并”,它将变得更加有意义。
If you are interested in diving deeper into the subject be sure to check out these references:
如果您有兴趣深入研究该主题,请务必查看以下参考资料:
The introduction to Reactive Programming you’ve been missing
您缺少的React式编程简介
One last topic I think is relevant to discuss before we dive into Combine is Functional Reactive Programming (FRP), which is no more than the combination of the Functional and Reactive paradigms.
我认为在进入“合并”之前需要讨论的最后一个主题是“功能性React式编程(FRP)” ,它仅是功能性和React性范式的组合。
Ok, so now that we got the basics covered, let’s go back to the topic that brought us here in the first place.
好的,现在我们已经涵盖了基础知识,让我们回到最初将我们带到这里的主题。
By putting all of these concepts together we can say in simple terms that Combine is Apple’s approach to a functional reactive programming framework.
通过将所有这些概念放在一起,我们可以简单地说,“合并”是Apple开发的功能性React式编程框架的方法。
Now, like any other framework, there are some core objects that allow you to interact with it, in this case, we are talking about: Publishers, Subscribers, and Operators.
现在,像其他任何框架一样,有一些核心对象允许您与其进行交互,在这种情况下,我们正在谈论的是: Publishers , Subscribers和Operators 。
As we said before, with combine we can process values over time, and a publisher is the object responsible for delivering these values. When new data becomes available, a publisher emits an event indicating this to anything that might be listening to it.
如前所述,借助合并,我们可以随着时间的推移处理价值,而发布者是负责交付这些价值的对象。 当有新数据可用时,发布者将发出一个事件,向可能正在侦听它的任何人指示此事件。
Technically speaking, Publisher is defined as a protocol with two associated types: Output and Failure. They represent the type of data this object can emit into the stream and as you can imagine, the Output data type is emitted when everything is going as it should, but for those cases when things go wrong, the Failure type allows us to pass down an error.
从技术上讲, Publisher被定义为具有两种关联类型的协议: Output和Failure 。 它们表示此对象可以向流中发送的数据类型,并且可以想象,在一切正常进行时将输出Output数据类型,但是对于出现错误的情况,使用Failure类型可以使我们向下传递一个错误。
Similarly, there are three different events this object can emit:
同样,此对象可以发出三种不同的事件:
An Output event when new data is available.
新数据可用时的Output事件。
A Completion event that indicates the object fulfilled its purpose and will stop emitting events.
指示对象已实现其目的的完成事件,并将停止发出事件。
A Failure event that provides an error of the associated type. This event has the same result as the Completion one, after emitting it the object is not allowed to publish any further events.
一个失败事件,提供关联类型的错误。 此事件与完成事件具有相同的结果,发出该事件后,该对象将不允许发布任何其他事件。
Since we are talking about reactive programming, when something happens, something else needs to react to it. In Combine, this something else is known as a Subscriber. This object is the one that receives the events emitted by the publishers and reacts to them however it sees fit.
由于我们正在谈论React式编程,因此当发生某些事情时,需要其他一些事情来对此做出React。 在Combine中,这又称为S ubscriber。 该对象是接收发布者发出的事件并对其做出React的对象,但是它认为合适。
As with the Publisher, Subscriber is defined as a protocol with two associated types: Input and Failure. The input corresponds to the type of data this object allows to receive and the failure the type of error.
与发布者一样,订户被定义为具有两种关联类型的协议:输入 和失败。 输入对应于此对象允许接收的数据类型,而失败对应于错误的类型。
An operator is an object that conforms to both Publisher and Subscriber protocols. That means that it can both emit and receive events.
运算符是既符合发布者协议又符合订阅者协议的对象。 这意味着它既可以发出也可以接收事件。
This is the object that will allow us to work with the data in the stream, by chaining multiple operators before finally handing it out to the last subscriber that will consume the end result.
这个对象将使我们能够处理流中的数据,方法是先链接多个运算符,然后再将其最终分发给将消耗最终结果的最后一个订户。
It might not be clear now what’s the purpose of it but bear with me for now and I promise it will make sense in just a moment.
现在可能还不清楚目的是什么,但请耐心等待,我保证一会儿就可以理解。
Ok, now we know the three main characters in this play, but, how does the play look like?
好的,现在我们知道了该剧中的三个主要角色,但是,该剧的模样如何?
Let’s distant ourselves for a second from the technical details and let’s imagine we work at a cookies store. In this store, we have two rooms: one where the kitchen is and a second where we serve our customers.
让我们与技术细节相距一秒钟,让我们想象一下我们在Cookie商店中工作。 在这家商店中,我们有两个房间:一个房间是厨房,另一个房间是我们为客户提供服务。
Now, in order to provide the best culinary experience to our clients, we only sell freshly made cookies, which means that every time a person enters the store and buys a cookie, the counter asks the kitchen to provide them.
现在,为了向客户提供最佳的烹饪体验,我们只出售新鲜制作的饼干,这意味着每次有人进入商店并购买饼干时,柜台都会要求厨房提供它们。
When this happens the kitchen starts the production line to make the cookies that go like this:
发生这种情况时,厨房将启动生产线以制作如下的Cookie:
Get the cookie dough. 得到饼干面团。 Shape it into a cookie. 将其成型为Cookie。 Add some nice chocolate chips. 加入一些美味的巧克力片。 Put them into the oven until cooked. 将它们放入烤箱中直至煮熟。 Deliver them to the counter to be handed out to the customer. 将它们交付给柜台,再分发给客户。Working in this store wouldn’t be good for our diet, but it sure gives us what we need to understand how Combine works.
在这家商店工作虽然不利于我们的饮食,但可以肯定的是,这为我们提供了了解Combine的工作原理所需的知识。
Think it through, we said we had three main objects, a publisher responsible for emitting events, a subscriber that consumes those events, and operators that behave like both and allow us to work with the data. If we try to map these items into the different roles of our store we could say:
仔细考虑一下,我们说我们有三个主要对象,一个负责发布事件的发布者,一个负责处理这些事件的订阅者,以及两个行为相似且允许我们处理数据的运算符。 如果我们尝试将这些商品映射到商店的不同角色,我们可以说:
The person at the counter would be a subscriber.
柜台上的人就是订户。
The person that gets the dough would be our publisher.
拿到面团的人将是我们的出版商。
Each person in the production line would be an operator.
生产线中的每个人都是一名操作员。
You might be thinking that if we need a different employee for each step in the production line for this simple store, how are we making a profit? Well, the business details go a bit beyond the scope of today’s topic, but it for sure helps us understand how an operator behaves as both publisher and subscriber at the same time.
您可能会想,如果对于这个简单的商店,生产线的每个步骤都需要一名不同的员工,那么我们如何获利呢? 嗯,业务细节超出了今天的主题范围,但是可以肯定地帮助我们了解运营商如何同时充当发布者和订阅者。
If we look at the production line we mentioned above and try to define them technically, we would end up with the following:
如果我们看一下上面提到的生产线并尝试从技术上对其进行定义,我们将得到以下结果:
- Publisher<CookieDough, CookieError> - Operator<(CookieDough, CookieError), (RawCookie, CookieError)> - Operator<(RawCookie, CookieError), (RawChocolateCookie, CookieError)> - Operator<(RawChocolateCookie, CookieError), (ChocolateCookie, CookieError)> - Subscriber<ChocolateCookie, CookieError>NOTE: The operator is both Publisher and Subscriber, that’s why in the interface we define the Input and Output types.
注意:运算符既是发布者又是订阅者,这就是为什么在界面中我们定义输入和输出类型的原因。
Operator<(Input, InputFailure), (Output, OutputFailure)>
运算符<(输入,InputFailure),(输出,OutputFailure)>
I mentioned above that the operator will allow us to do some work with the data. So, in this example, we see how we were able to chain them in order to create a data stream that transformed the initial data on each step until it finally delivered the object the subscriber was expecting.
我在上面提到过,运算符将允许我们对数据进行一些处理。 因此,在此示例中,我们看到了如何链接它们以创建一个数据流,该数据流在每个步骤上转换初始数据,直到最终交付用户期望的对象。
Something we see in the example above is that it’s the subscriber who starts the whole process, rather than the publisher. This might be confusing as we could expect it to be the other way around. Instead of the subscriber asking for data, the publisher delivering data for the subscriber to process.
在上面的示例中,我们看到的是启动整个过程的是订户,而不是发布者。 这可能会令人困惑,因为我们可以预料相反。 发布者向订阅者发送数据以供其处理,而不是订阅者请求数据。
Well, the reason for this is that Combine inverts the relationship between these objects in a concept defined as back-pressure. What this means is that the subscriber controls the data stream by informing the publisher about how much data it needs or it can handle at a specific moment.
好吧,这样做的原因是Combine在定义为背压的概念中反转了这些对象之间的关系。 这意味着订户通过通知发布者它需要多少数据或在特定时刻可以处理多少数据来控制数据流。
In simpler terms, it’s not the kitchen employee telling the counter how many cookies they have in inventory. It’s the counter employee asking the kitchen for X amount of cookies.
简单来说,这不是厨房员工告诉柜台他们库存的饼干数量。 是柜台员工向厨房要X倍的饼干。
We saw the role of each actor in the play, but we still don’t know the script of the play, so let’s have a look at it.
我们看到了剧中每个演员的角色,但是我们仍然不知道剧本的剧本,所以让我们来看看它。
Raywenderlich Combine: Getting Started Raywenderlich结合:入门In the image above we can see the lifecycle of the relationship between a publisher and a subscriber.
在上图中,我们可以看到发布者和订阅者之间关系的生命周期。
A Subscriber is attached to a Publisher by calling .subscribe(_: Subscriber)
订阅者通过调用.subscribe(_:订阅者)附加到发布者
The Publisher acknowledges the subscription by returning through receive(subscription: Subscription) a subscription to the Subscriber.
发布者通过接收(订阅:Subscription)对订阅者的订阅来确认订阅。
At this point, the Subscriber informs the Publisher of how much data (X) it needs from it. This could be either a finite or infinite amount.
此时,订阅服务器将其需要多少数据( X )通知发布服务器。 这可以是有限的或无限的量。
The Publisher may then (as it has values) send X (or fewer) values using receive(_: Input). A Publisher should never send more than the demand requested.
然后,发布者可以(因为具有值)使用receive(_:Input)发送X个(或更少)值。 发布者发送的内容绝不应超过请求的需求。
At some point, the Publisher may optionally send a completion event. receive(completion:). This completion event will be either a normal termination or a failure propagating an error with it as we mentioned above.
在某个时候,发布者可以选择发送完成事件。 接收(完成:) 。 如上所述,完成事件将是正常终止,也可能是失败并传播错误。
Because we want to be good Combine citizens, we need to pay attention to our memory footprint. Every time we connect a subscriber with a publisher a strong reference is born between them in order to keep the relationship alive. This is being controlled by yet another protocol called Cancellable.
因为我们想成为好公民,所以我们需要注意我们的记忆足迹。 每次我们将订阅者与发布者联系起来时,他们之间就会产生一个强大的参考,以保持这种关系。 这由另一个称为Cancellable的协议控制。
An object conforming to this protocol is returned upon every successful subscription and for as long as this object remains in memory, the subscriber and the publisher will continue to talk to each other, but at the moment this object decides to cancel() it or it gets deallocated, then the relationship between our actors will cease to exist as well.
每次成功订阅后都会返回一个符合此协议的对象,并且只要该对象保留在内存中,订阅者和发布者就会继续互相交谈,但是此对象决定立即将其取消(或取消)被释放,那么我们演员之间的关系也将不复存在。
I will explain further how this object looks like and how to work with it on the next part of the series. For now, just keep in mind that it exists and that it will help us a lot with our memory management.
在本系列的下一部分,我将进一步解释该对象的外观以及如何使用它。 现在,请记住它的存在,它将对我们的内存管理有很大帮助。
That was a lot of information. Take a moment to digest all of it and I’ll see you in the next part where I will show you how all of it fits together at a technical level.
那是很多信息。 请花一点时间来消化所有内容,我将在下一部分与您见面,向您展示如何在技术水平上将它们融合在一起。
翻译自: https://medium.com/dev-jam/combine-from-zero-to-oh-i-get-it-part-i-6aa9ced8e5f
排列组合从零开始