Combinar: conceptos básicos

Como se mencionó en la descripción anterior , Combine tiene tres conceptos básicos: publicador, operador y suscriptor. Publisher y Subscriber representan publicadores y suscriptores de eventos, respectivamente. Operator tiene las características de ambos. Sigue los protocolos Subscriber y Publisher y se utiliza para operar datos upstream.

Siempre que comprenda estos tres conceptos básicos, puede usar Combine muy bien, por lo que desde esta perspectiva, podemos simplemente entender Combine de la siguiente forma:

Combinar = Editores + Operadores + Suscriptores

Editor

definición:

public protocol Publisher<Output, Failure> {

    associatedtype Output
    associatedtype Failure : Error

    func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
复制代码

Una vez que el editor se haya suscrito, proporcionará datos de acuerdo con la solicitud del suscriptor. Un editor sin ningún suscriptor no enviará ningún dato.

El editor puede publicar tres tipos de eventos:

  • Salida: el nuevo valor que aparece en el flujo de eventos
  • Terminado: todos los elementos del flujo de eventos se han liberado y el flujo de eventos ha completado su misión y ha finalizado.
  • Error: se produjo un error en el flujo de eventos y el flujo de eventos termina aquí

Los eventos Finalizados y Fallidos se definen en Subscribers.Completion

extension Subscribers {
    @frozen public enum Completion<Failure> where Failure : Error {
        case finished
        case failure(Failure)
    }
}
复制代码

No se requiere ninguno de los tres eventos. Un publicador puede o no emitir uno o más valores de salida; puede que nunca se detenga, o puede señalar la finalización emitiendo un evento de falla o finalización .

Una secuencia de eventos que finalmente terminará se denomina secuencia finita de eventos, mientras que una secuencia de eventos que no falla ni finaliza se denomina secuencia infinita de eventos. Por ejemplo, una solicitud de red es un flujo de eventos finito y un flujo de eventos de clic de botón es un flujo de eventos infinito.

Sujeto

El sujeto también es un editor

public protocol Subject : AnyObject, Publisher {
	func send(_ value: Self.Output)
	func send(completion: Subscribers.Completion<Self.Failure>)
}
复制代码

Subject 暴露了两个 send 方法,外部调用者可以通过这两个方法来主动地发布 output 值、failure 事件或 finished 事件。Subject可以将传统的指令式编程中的异步事件和信号转换到响应式的世界中去

Combine内置了两种Subject类型:

  • PassthroughSubject

    简单地将 send 接收到的事件转发给下游的其他 Publisher 或 Subscriber,不会持有最新的output;如果在订阅前执行send操作,是无效的。

let publisher1 = PassthroughSubject<Int, Never>()
print("开始订阅")
publisher1.sink(
	receiveCompletion: { complete in
		print(complete)
	},
	receiveValue: { value in
		print(value)
    })
publisher1.send(1)
publisher1.send(2)
publisher1.send(completion: .finished)
// 输出:
// 开始订阅
// 1
// 2
// finished
复制代码

调整一下 sink 订阅的时机,将它延后到 publisher.send(1) 之后,那么订阅者将会从 2 的事件开始进行响应:

let publisher2 = PassthroughSubject<Int, Never>()
publisher2.send(1)
print("开始订阅")
publisher2.sink(
	receiveCompletion: { complete in
		print(complete)
	},
	receiveValue: { value in
		print(value)
              })
publisher2.send(2)
publisher2.send(completion: .finished)
// 输出:
// 开始订阅
// 2
// finished
复制代码
  • CurrentValueSubject

    会包装和持有一个值,并在设置该值时发送事件并保留新的值。在订阅发生的瞬间,会把当前保存的值发送给订阅者;接下来对值的每次设置都将触发订阅响应。

let publisher3 = CurrentValueSubject<Int, Never>(0)
print("开始订阅")
publisher3.sink(
	receiveCompletion: { complete in
		print(complete)
	},
	receiveValue: { value in
		print(value)
    })
publisher3.value = 1
publisher3.value = 2
publisher3.send(completion: .finished)
// 输出:
// 开始订阅
// 0
// 1
// 2
// finished
复制代码

Subscriber

定义:

public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible {

    associatedtype Input
    associatedtype Failure : Error

    func receive(subscription: Subscription)

    func receive(_ input: Self.Input) -> Subscribers.Demand

    func receive(completion: Subscribers.Completion<Self.Failure>)
}
复制代码

想要订阅某个 Publisher,Subscriber 中的这两个类型必须与 Publisher 的 Output 和 Failure 一致。

Combine 中也定义了几个比较常见的 Subscriber,可以供我们直接使用。

sink

sink的完整函数签名为

func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
复制代码

receiveCompletion 用来接收 failure 或者 finished 事件,receiveValue 用来接收 output 值。

let just = Just("Hello word!")
    _ = just.sink(receiveCompletion: {
        print("Received completion", $0)
    }, receiveValue: {
        print("Received value", $0)
    })
复制代码

如果说Subject提供了一条从指令式异步编程通向响应式世界的道路的话,那么sink就补全了另外一侧。sink可以作为响应式代码和基于闭包的指令式代码之间的桥梁,让你可以通过它从响应式的世界中回到指令式的世界。因为receiveValue闭包会将值带给你,想要对它做什么就随你愿意了。

assign

func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root,Self.Output>, on object:Root) -> AnyCancellable
复制代码

assign 接受一个 class 对象以及对象类型上的某个键路径 (key path)。每当 output 事件到来时,其中包含的值就将被设置到对应的属性上去。

定义一个MyObject类

class MyObject {
	var value: String = "123" {
		didSet {
			print(value)
		}
	}
}
复制代码

使用 assign(to:on:)修改MyObject实例对象属性的值

let obj = MyObject()
let _ = ["456"].publisher.assign(to: \.value, on: obj)
复制代码

assign 还有一个变体, assign(to:) 可将 Publisher 发出的值用于 @Published 属性包装器包装过的属性

class MyObject {
	@Published var value = 0
}

let objc = MyObject()
objc.$value.sink {		
	print($0)
}

(0 ..< 5).publisher.assign(to: &objc.$value)
复制代码

value 属性用 @Published包装,除了可作为常规属性访问之外,它还为属性创建了一个 Publisher。使用 @Published 属性上的 $ 前缀来访问其底层 Publisher,订阅该 Publisher,并打印出收到的每个值。最后,我们创建一个 0..<5 的 Int Publisher 并将它发出的每个值 assign 给 object 的 value Publisher。 使用 & 来表示对属性的 inout 引用,这里的 inout 来源于函数签名:

func assign(to published: inout Published<Self.Output>.Publisher)
复制代码

这里有一个值得注意的地方,如果使用 assign(to: .value, on: self) 并存储生成的 AnyCancellable,可能会引起引用循环:MyObject 类实例持有生成的 AnyCancellable,而生成的 AnyCancellable 同样保持对 MyObject 类实例的引用。因此,推荐使用 assign(to:) 来替代 assign(to:on:) ,以避免此问题的发生,因为assign(to:) ::不返回 AnyCancellable,在内部完成了生命周期的管理,在 @Published 属性释放时会取消订阅。::

Operator

关于Operator的介绍,在概览中已经做了相对详细的介绍。

Los operadores se pueden usar como entrada para los publicadores ascendentes y también pueden convertirse en nuevos publicadores que envían datos procesados ​​a los descendentes. Podemos combinar diferentes operadores para formar una cadena de procesamiento: cuando el Publicador en la parte superior de la cadena publica eventos o datos, los Operadores de la cadena procesarán estos datos y eventos paso a paso, y finalmente alcanzarán el resultado especificado por el suscriptor.

Con respecto a los operadores de uso común, este artículo es muy completo y puede usarse como referencia.

referencia

Combine: Programación asíncrona con Swift

Programación SwiftUI y Combine

Supongo que te gusta

Origin juejin.im/post/7200653914818691131
Recomendado
Clasificación