react 编程式路由_什么是React式编程? iOS版

react 编程式路由

There are many articles about Reactive Programming and different implementations on the internet. However, most of them are about practical usage, and only a few concern what Reactive Programming is, and how it actually works. In my opinion, it is more important to understand how frameworks work deep inside — spoiler: nothing actually complicated there — rather than starting to use a number of traits and operators meanwhile shooting yourself in the foot.

互联网上有很多关于响应式编程和不同实现的文章。 但是,它们中的大多数都是关于实际使用的,只有很少的一部分涉及什么是React式编程及其实际工作方式。 在我看来,更重要的是了解框架如何深入内部工作—破坏者:实际上没有什么复杂的东西—而不是开始使用许多特征和操作员同时使自己陷入困境。

So, what is RxSwift Combine Reactive programming?

那是什么 RxSwift 结合 React性编程?

According to Wikipedia:

根据维基百科:

Reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm, it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.

Excuse me, WHAT?

不好意思

Let's start from the beginning.

让我们从头开始。

Reactive programming is an idea from the late 90s that inspired Erik Meijer, a computer scientist at Microsoft, to design and develop the Microsoft Rx library, but what is it exactly?

响应式编程是90年代末期的一种思想,它启发了Microsoft的计算机科学家Erik Meijer来设计和开发Microsoft Rx库,但是它到底是什么呢?

I don't want to provide one definition of what reactive programming is. I would use the same complicated description as Wikipedia. I think it's better to compare imperative and reactive approaches.

我不想为React式编程提供一个定义。 我将使用与Wikipedia相同的复杂描述。 我认为最好比较命令式和被动式方法。

With an imperative approach, a developer can expect that the code instructions will be executed incrementally, one by one, one at a time, in order as you have written them.

使用命令式方法,开发人员可以期望代码指令将按照您编写的顺序逐一,逐个递增地执行。

The reactive approach is not just a way to handle asynchronous code; it's a way to stop thinking about threads, and start to think about sequences. It allows you to treat streams of asynchronous events with the same sort of simple, composable operations that you use for collections of data items like arrays. You think about how your system reacts to the new information. In simple words, our system is always ready to handle new information, and technically the order of the calls is not a concern.

React性方法不仅仅是处理异步代码的一种方法; 这是一种停止考虑线程,而开始考虑序列的方法。 它使您可以使用与用于数据项(如数组)集合相同的简单,可组合的操作来处理异步事件流。 您考虑系统如何对新信息reacts 。 简而言之,我们的系统随时准备处理新信息,从技术上讲,呼叫的顺序无关紧要。

I assume that most of the readers of this article came from iOS development. So let me make an analogy. Reactive programming is Notification center on steroids, but don't worry, a counterweight of the reactive frameworks is that they are more sequential and understandable. Moreover in iOS development, it's hard to do things in one way, because Apple gave us several different approaches like delegates, selectors, GCD and etc. The reactive paradigm could help solve these problems in one fashion.

我认为本文的大多数读者来自iOS开发。 因此,让我做个比喻。 响应式编程是类固醇的通知中心,但请不要担心,响应式框架的平衡点在于它们的顺序性和可理解性更高。 此外,在iOS开发中,很难以一种方式完成事情,因为Apple为我们提供了几种不同的方法,例如委托,选择器,GCD等。被动式范例可以以一种方式帮助解决这些问题。

It sounds quite simple. Let's take a look ar a couple of functions in one class implementation of one of the most popular frameworks RxSwift:

听起来很简单。 让我们看一下最流行的框架之一RxSwift一个类实现中的几个函数:

public final class BehaviorSubject<Element> {

    public func value() throws -> Element {
        self._lock.lock(); defer { self._lock.unlock() }
            if self._isDisposed {
                throw RxError.disposed(object: self)
            }

            if let error = self._stoppedEvent?.error {
                throw error
            }
            else {
                return self._element
            }
    }

    func _synchronized_on(_ event: Event<Element>) -> Observers {
        self._lock.lock(); defer { self._lock.unlock() }
        if self._stoppedEvent != nil || self._isDisposed {
            return Observers()
        }

        switch event {
        case .next(let element):
            self._element = element
        case .error, .completed:
            self._stoppedEvent = event
        }

        return self._observers
    }
}

This even partial example does not look easy at all… As we can see the implementation of RxSwift is not so simple. But let me explain myself. RxSwift is an advanced, highly optimized framework with wide functionality. To understand the principles of the reactive world, this framework doesn't fit. So, what are we going to do? We are going to write our own reactive solution from scratch. To do this, firstly we need to understand which parts this library consists of.

这个甚至是部分示例也不是一件容易的事……正如我们所看到的, RxSwift的实现不是那么简单。 但是,让我自己解释一下。 RxSwift是具有广泛功能的高级,高度优化的框架。 要了解React世界的原理,此框架不适合。 那么,我们该怎么办? 我们将从头开始编写我们自己的React性解决方案。 为此,首先我们需要了解该库包含哪些部分。

两个朋友的故事 (The tale of two friends)

Let me answer again the question: What is reactive programming? Reactive programming is a friendship of two design patterns: Iterator and Observer. Let's have a quick reminder of how these patterns work.

让我再回答一个问题:什么是React式编程? 响应式编程是两种设计模式的友谊: IteratorObserver 。 让我们快速提醒一下这些模式是如何工作的。

Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.). You can read more at this link.

Iterator是一种行为设计模式,可让您遍历集合的元素而无需暴露其基础表示(列表,堆栈,树等)。 您可以在此链接上阅读更多内容。

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing. You can read more at this link.

Observer是一种行为设计模式,可让您定义订阅机制,以通知多个对象有关观察对象发生的任何事件。 您可以在此链接上阅读更多内容。

How do these two friends work together? In simple terms, you use the Observer pattern to be subscribed for new events, and use the Iterator pattern to treat streams like sequences.

这两个朋友如何一起工作? 简单来说,您可以使用Observer模式订阅新事件,并使用Iterator模式将流视为序列。

Iterator

迭代器

Let's start from the beginning. From the Iterator pattern.

让我们从头开始。 从Iterator模式开始。

Here's a simple sequence of integers:

这是一个简单的整数序列:

let sequence = [1, 2, 3, 4, 5]

And I want to iterate through it. Easy enough:

我想遍历它。 很简单:

var iterator = sequence.makeIterator()

while let item = iterator.next() {
    print(item)
}

// 1 2 3 4 5

However, I think that everybody would say this way of iteration via sequence is a little bit weird. Let's do this the proper way:

但是,我认为每个人都会说这种通过序列进行迭代的方法有点怪异。 让我们以正确的方式执行此操作:

sequence.forEach { item in
    print(item)
}

// 1 2 3 4 5

For now, it looks more natural, or at least I hope so. I used the forEach method on purpose. forEach has this signature func forEach(_ body: (Element) -> Void). It's a function which takes a function(handler) as an argument and performs this handler over the sequence. Let's try to build forEach by ourselves.

就目前而言,它看起来更加自然,或者至少我希望如此。 我故意使用了forEach方法。 forEach具有此签名func forEach(_ body: (Element) -> Void) 。 这个函数将一个function(handler)作为参数,并在序列上执行此处理程序。 让我们尝试自己构建forEach

extension Array {
    func forEach(_ body: @escaping (Element) -> Void) {
        for element in self {
            body(element)
        }
    }
}

sequence.forEach {
    print($0)
}

// 1 2 3 4 5

With forEach semantics it's possible to write this elegant code.

使用forEach语义,可以编写这种精美的代码。

func handle(_ item: Int) {
    print(item)
}

sequence.forEach(handle)

// 1 2 3 4 5

As I said before, that reactive programming is above all thread problems. Let's add to our custom forEach some thread abstraction.

正如我之前说过的,React式编程首先是线程问题。 让我们为forEach线程添加一些线程抽象。

extension Array {
    func forEach(
        on queue: DispatchQueue,
        body: @escaping (Element) -> Void) {
        for element in self {
            queue.async { body(element) }
        }
    }
}

let queue = DispatchQueue(
    label: "com.reactive",
    qos: .background,
    attributes: .concurrent
)

sequence.forEach(on: queue, body: handle)

// Output is unpredictable, but we'd see all 5 values.

Observer

观察者

I went so far and did some strange custom forEach for Array. What is this for? We'll know about this a little bit later, but now let's move to Observer.

到目前为止,我对Array的forEach做了一些奇怪的定制。 这个是来做什么的? 稍后我们将对此有所了解,但是现在让我们转到Observer

There are many terms used to describe this model of asynchronous programming and design. This article will use the following terms: an Observer and Observable. An Observer subscribes to an Observable, and the Observable emits items or sends notifications to its observers by calling the observers’ methods.

有许多术语用于描述这种异步编程和设计模型。 本文将使用以下术语: ObserverObservableObserver订阅了一个Observable ,并且Observable通过调用观察者的方法来发出项目或向其观察者发送通知。

In other words: Observable is a stream with data itself, and Observer is a consumer of this stream.

换句话说: Observable是具有数据本身的流,而Observer是该流的使用者。

Let's start with the Observer. As I said, it's a consumer of a data stream, which can do something around this data. Let me translate, it's a class with a function inside, which calls when new data arrives. Let’s implement this class.:

让我们从Observer开始。 正如我所说的,它是数据流的使用者,它可以对这些数据进行处理。 让我翻译一下,它是一个内部带有函数的类,当有新数据到达时调用此函数。 让我们实现这个类。

class Observer<Element> {

    private let on: (Element) -> Void

    init(_ on: @escaping (Element) -> Void) {
        self.on = on
    }

    func on(_ event: Element) {
        on(event)
    }
}

And now let’s move to Observable. Observable it's data itself. Let's make it simple for the first iteration.

现在让我们转到ObservableObservable到的是数据本身。 让我们简化第一次迭代。

class Observable<Element> {

    var value: Element

    init(value: Element) {
        self.value = value
    }
}

The most interesting part is that Observable should allow to subscribe to a consumer of this data. And via changing this data in Observable, Observer needs to know about these changes.

最有趣的部分是Observable应该允许subscribe此数据的使用者。 通过在Observable更改此数据, Observer需要了解这些更改。

class Observable<Element> {
    private var observers: [Observer<Element>] = []

    var value: Element {
        didSet {
            observers.forEach { $0.on(self.value) }
        }
    }

    init(value: Element) {
        self.value = value
    }

    func subscribe(on observer: Observer<Element>) {
        observers.append(observer)
    }
}

Actually we just build our Observer pattern. So, let's try this out.

实际上,我们只是构建了Observer模式。 因此,让我们尝试一下。

let observer = Observer<Int> {
    print($0)
}

let observable = Observable<Int>(value: 0)
observable.subscribe(on: observer)

for i in 1...5 {
    observable.value = i
}

// 1, 2, 3, 4, 5

And it works! But hold on for a second — let's add some modifications before we go further.

而且有效! 但是,请稍等一下-在继续之前,我们要进行一些修改。

Maybe you've already mentioned that our Observable stores all input Observers via subscription, which is not so great. Let's make this dependency weak. However, Swift doesn't support weak arrays for now and maybe forever, that's why we need to handle this situation otherwise. Let's implement the class wrapper with a weak reference in it.

也许您已经提到过,我们的Observable通过订阅存储所有输入的Observers ,但这并不是很好。 让我们将此依赖项weak 。 但是,Swift暂时或可能永远不支持弱数组,这就是为什么我们需要另外处理这种情况的原因。 让我们用一个弱引用实现类包装器。

class WeakRef<T> where T: AnyObject {

    private(set) weak var value: T?

    init(value: T?) {
        self.value = value
    }
}

As a result, you can see a generic object, which could hold other objects weakly. Now let's make some improvements to Observable.

结果,您可以看到一个通用对象,该对象可能会弱地容纳其他对象。 现在让我们对Observable进行一些改进。

class Observable<Element> {
    private typealias WeakObserver = WeakRef<Observer<Element>>
    private var observers: [WeakObserver] = []

    var value: Element {
        didSet {
            observers.forEach { $0.value?.on(self.value) }
        }
    }

    init(value: Element) {
        self.value = value
    }

    func subscribe(on observer: Observer<Element>) {
        observers.append(.init(value: observer))
    }
}

For now Observers not held by Observable. Let's try this out and create two observers.

目前, Observable尚未持有Observer 。 让我们尝试一下,创建两个观察者。

let observer1 = Observer<Int> {
    print("first:  ", $0)
}

var observer2: Observer! = Observer<Int> {
    print("second: ", $0)
}

let observable = Observable<Int>(value: 0)
observable.subscribe(on: observer1)
observable.subscribe(on: observer2)

for i in 1...5 {
    observable.value = i

    if i == 2 {
        observer2 = nil
    }
}

/*
first:   1
second:  1
first:   2
second:  2
first:   3
first:   4
first:   5
*/

As you can see, the second Observer was destroyed after 2, which proves the workability of the code. However, I think creating an Observer object by hand all the time could be annoying, so let's improve Observable to consume a closure, not an object.

如您所见,第二个Observer2之后被销毁,这证明了代码的可操作性。 但是,我认为一直手工创建一个Observer对象可能很烦人,因此让我们改进Observable来使用闭包而不是对象。

class Observable<Element> {
    private typealias WeakObserver = WeakRef<Observer<Element>>
    private var observers: [WeakObserver] = []

    var value: Element {
        didSet {
            observers.forEach { $0.value?.on(self.value) }
        }
    }

    init(value: Element) {
        self.value = value
    }

    func subscribe(onNext: @escaping (Element) -> Void) -> Observer<Element> {
        let observer = Observer(onNext)
        observers.append(.init(value: observer))
        return observer
    }
}

let observable = Observable<Int>(value: 0)
let observer = observable.subscribe {
    print($0)
}

for i in 1...5 {
    observable.value = i
}

// 1, 2, 3, 4, 5

For my taste usage is more clear now, however it's possible to use both subscribe functions.

因为我的口味用法现在更加清楚,但是可以同时使用两个subscribe功能。

For now, our tiny reactive framework looks finished, but not exactly. Let's do some asynchronous stress tests for the Observable.

目前,我们微小的React框架看起来已经完成,但还不完全。 让我们对Observable进行一些异步压力测试。

let observable = Observable<Int>(value: 0)
let observer = observable.subscribe {
    print($0)
}

for i in 1...5 {
    DispatchQueue(label: "1", qos: .background, attributes: .concurrent).asyncAfter(deadline: .now() + 0.3) {
        observable.value = i
    }
}

for i in 6...9 {
    DispatchQueue(label: "2", qos: .background, attributes: .concurrent).asyncAfter(deadline: .now() + 0.3) {
        observable.value = i
    }
}

In this case, we should receive numbers from 1 to 9 in random order, because changes run in the different asynchronous queues. For my case, it was like this

在这种情况下,我们应该以随机顺序接收1到9之间的数字,因为更改是在不同的异步队列中进行的。 就我而言,就是这样

/*
3
4
4
4
5
6
7
8
9
*/

As you can see, it's not the expected result. A race condition happened and it should be fixed. The solution is easy — let's add some thread synchronization. There are several ways to achieve this, but I'll use a method with a dispatch barrier. Here's the solution.

如您所见,这不是预期的结果。 发生了比赛情况,应予以解决。 解决方案很简单-让我们添加一些线程同步。 有几种方法可以实现此目的,但是我将使用一种带有调度屏障的方法。 这是解决方案。

class Observable<Element> {
    private typealias WeakObserver = WeakRef<Observer<Element>>
    private var observers: [WeakObserver] = []
    private let isolationQueue = DispatchQueue(label: "", attributes: .concurrent)

    private var _value: Element
    var value: Element {
        get {
            isolationQueue.sync { _value }
        }
        set {
            isolationQueue.async(flags: .barrier) {
                self._value = newValue
                self.observers.forEach { $0.value?.on(newValue) }
            }
        }
    }

    init(value: Element) {
        self._value = value
    }

    func subscribe(onNext: @escaping (Element) -> Void) -> Observer<Element> {
        let observer = Observer(onNext)
        observers.append(.init(value: observer))
        return observer
    }
}

The same test as before gave me this result:

和以前一样的测试给了我这个结果:

/*
1
2
3
4
5
6
7
8
9
*/

This time it's even in the right order, but be aware that it's not guaranteed. Now our reactive framework has thread synchronization.

这次,即使顺序正确,但请注意,不能保证。 现在我们的React式框架具有线程同步。

Let's move further and there's another difference between a vanilla Observer pattern and most of the reactive frameworks. Usually, as an Element from Observable, you manipulate not just an Element, but some kind of Event enumeration, which looks like this.

让我们继续前进,香草Observer模式与大多数React式框架之间还有另一个区别。 通常,作为ObservableElement ,您不仅要操纵Element ,而且要操纵某种Event枚举,如下所示。

enum Event<Element> {
    case next(Element)
    case completed
    case error(Error)
}

It’s a handy solution, because you can handle situations when your sequence completed or received an error. I don't want to spend time adopting this practice right now, I think it doesn't matter for concept understanding.

这是一个方便的解决方案,因为您可以处理序列完成或收到错误时的情况。 我现在不想花时间采取这种做法,我认为这对概念理解并不重要。

Let's compose Observer and Iterator

让我们组成ObserverIterator

One of the killer features for reactive programming is the possibility to treat your Observable sequence as a Sequence I think everybody knows these handy functions like map, flatMap, reduce, and so on. As an example, let's try to add to our Observable the map function. But firstly let's remember how it works with a simple array.

React式编程的杀手级功能之一是可以将Observable sequence视为Sequence我认为每个人都知道这些方便的功能,例如mapflatMapreduce等。 例如,让我们尝试将map函数添加到Observable中。 但是首先让我们记住它如何与简单数组一起工作。

let sequence = [1, 2, 3, 4, 5]
let newSequence = sequence
    .map { element in
        return element + 1
}

// newSequence: 2, 3, 4, 5, 6

This case is a primitive adding 1 to every element. Can we do the same with an Observable? Sure we can. Let's add a map function to our Observable.

这种情况是一个基本元素,每个元素加1。 我们可以对Observable做同样的事情吗? 我们当然可以。 让我们向Observable添加一个map函数。

class Observable<Element> {
    typealias WeakObserver = WeakRef<Observer<Element>>
    private var observers: [WeakObserver] = []
    private let isolationQueue = DispatchQueue(label: "", attributes: .concurrent)

    private var _value: Element
    var value: Element {
        get {
            isolationQueue.sync { _value }
        }
        set {
            isolationQueue.async(flags: .barrier) {
                self._value = newValue
                self.observers.forEach { $0.value?.on(newValue) }
            }
        }
    }
    private var transform: ((Element) -> Element)?

    init(value: Element) {
        self._value = value
    }

    func subscribe(onNext: @escaping (Element) -> Void) -> Observer<Element> {
        let transform = self.transform ?? { $0 }
        let observer = Observer<Element> { element in
            onNext(transform(element))
        }
        observers.append(.init(value: observer))
        return observer
    }

    func map(_ transform: @escaping (Element) -> Element) -> Observable<Element> {
        self.transform = transform
        return self
    }
}

let observable = Observable<Int>(value: 0)
let observer = observable
    .map { $0 + 1 }
    .subscribe { print($0) }

for i in 1...5 {
    observable.value = i
}

// 2, 3, 4, 5, 6

Yeah, you can mention that I've cheated a little bit.

是的,您可以提到我有点被骗了。

True map function would have structure with a generic like this:

真正的map函数具有这样的通用结构:

func map<T>(_ transform: @escaping (Element) -> T) -> Observable<T>

However, for the sake of simplicity in this article I just added this:

但是,为了简单起见,在本文中我仅添加了以下内容:

func map(_ transform: @escaping (Element) -> Element) -> Observable<Element>

I hope you could forgive me and understand the point.

我希望你能原谅我并理解这一点。

Actually, we're done for now with our own reactive framework, congratulations to everybody who followed until the end. It's super simplified but it works. Gist with the last iteration of this article you can find here.

实际上,到目前为止,我们已经有了自己的响应式框架,恭喜所有一直关注到最后的人。 它是超级简化的,但是可以工作。 要点与本文的最后迭代可以在这里找到。

I hope at least for now, reactive programming doesn’t look scary anymore. However, I hear all the time from people, that reactive way could lead us t an enormous number of sequences flying around the project and it's very easy to shoot yourself in the foot with this approach. I won't fight against this, and you can easily Google a bad style of doing reactive. I don't want to leave you with a cliffhanger, but I hope to show you a way, how to treat a reactive approach in the next chapters.

我希望至少到目前为止,React式编程看起来不再可怕。 但是,我一直都听到人们的声音,这种被动的方式可能会导致我们在项目中绕行大量的序列,并且使用这种方法很容易使自己陷入困境。 我不会与之抗争,您可以轻易地将Google的不良React风格化。 我不想让您大吃一惊,但我希望在下一章中向您展示一种如何对待React性方法的方法。

去哪里 (Where to go after)

翻译自: https://habr.com/en/post/497300/

react 编程式路由

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值