Combine #13:资源管理

发布: (2026年1月2日 GMT+8 23:57)
8 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的正文内容,我将为您翻译成简体中文。

Source:

share()

大多数 Combine 的 Publishers 都是 struct,它们仅描述一个 pipeline,并不保存共享状态。不会创建一个活跃的实例来持有数据,而是生成一个值,描述在有人订阅时如何产生流。因此,每一次订阅都会启动一个新的发布序列(即启动它自己的工作)。

share() 返回一个 Publisher.Share,它是基于 multicast(_:)PassthroughSubject 实现的,会创建 唯一一次 对原始 Publisher 的订阅,并在多个订阅者之间共享。由于 Combine 在内部使用类来保存对订阅者的引用,这就产生了共享状态

let shared = URLSession.shared
    .dataTaskPublisher(for: URL(string: "https://ejemplo.com")!)
    .map(\.data)
    .share()

let subscription1 = shared.sink { /* … */ }
let subscription2 = shared.sink { /* … */ }
// 只会发起一次网络请求,结果会在两个订阅之间共享

share() 收到第一个订阅时,会启动输入 Publisher 的工作。这可能导致流在其他订阅到来之前就已经结束(发送完成事件),此时后续的订阅只能收到完成事件。

let shared = URLSession.shared
    .dataTaskPublisher(for: URL(string: "https://ejemplo.com")!)
    .map(\.data)
    .share()

let subscription1 = shared.sink { /* … */ }
var subscription2: AnyCancellable? = nil

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    subscription2 = shared.sink { /* … */ }   // 只会收到完成事件
}

multicast(_:)

multicast(_:) 使用 Subject 向多个订阅者发出值,并返回一个 ConnectablePublisher,这意味着它只有在收到 connect() 信号时才会订阅输入的 Publisher。

// Subject para emitir eventos del multicast.
// Recibe del flujo de entrada
let subject = PassthroughSubject<Data, URLError>()

// Se crea el Publisher multicast, envolviendo el subject
let multicasted = URLSession.shared
    .dataTaskPublisher(for: URL(string: "https://ejemplo.com")!)
    .map(\.data)
    .multicast(subject: subject)

let subscription1 = multicasted.sink { /* … */ }
var subscription2: AnyCancellable? = nil

// Crear una segunda suscripción al multicast
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    subscription2 = multicasted.sink { /* … */ }
}

// Suscribir el multicast al dataTaskPublisher
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
    let cancellable = multicasted.connect()
}

由于 multicast(_:) 返回 ConnectablePublisher,也可以使用 autoconnect() 让它在收到第一个订阅时立即订阅输入的 Publisher。

在只需要执行一次工作并发出一个值的场景下,可以使用 CurrentValueSubject

Future

Future 是一个 Publisher(实现为 class,而不是 struct),它接受一个带有 Future.Promise 的闭包。该闭包只产生 一个值 然后结束。在闭包内部执行工作,完成后调用 promise 并传入结果或错误。

  • 在创建 Future 实例时,闭包会立即执行,不需要等待任何订阅者。
  • Future 会保存 promise 的结果,并向 所有 订阅者(包括当前和未来的)发送该结果。
let future = Future { fulfill in
    do {
        let result = try performSomeWork()
        fulfill(.success(result))
    } catch {
        fulfill(.failure(error))
    }
}

let subscription1 = future.sink(
    receiveCompletion: { _ in },
    receiveValue: { value in print("1️⃣", value) }
)

let subscription2 = future.sink(
    receiveCompletion: { _ in },
    receiveValue: { value in print("2️⃣", value) }
)

复习问题

Combine 的 Publisher,是否每个订阅都会重复流的工作?

  • 因为 Publisher 是保持内部状态的类。
  • 因为 Combine 为每个订阅创建了一个新线程。
  • 因为 Publisher 是结构体(struct),仅描述管道且不共享状态。
  • 因为订阅者必须手动使用 connect() 进行连接。

Publisher 中的 share()

  • 为每个订阅者创建独立的流副本。
  • 取消重复的订阅。
  • 对原始 Publisher 创建单一订阅,并在多个订阅者之间共享其结果。
  • 将 Publisher 转换为 Subject

使用 share() 的共享 Publisher 在流已经结束后进行

  • 接收之前存储的值。
  • 不接收任何值,只接收完成事件。
  • 重新执行整个原始流。
  • 导致运行时错误。

share() 用于哪里?

  • FutureJust 中。
  • multicast(_:)PassthroughSubject 中。
  • CurrentValueSubject 中。
  • DispatchQueue 中。

multicast(_:)share() 的比较

  • multicast(_:) 不需要 Subject
  • multicast(_:) 返回一个 ConnectablePublisher,需要通过 connect() 调用来连接。
  • multicast(_:) 为每个订阅者重复工作。
  • share() 允许多次手动连接,而 multicast(_:) 不允许。

使用 multicast(_:) 创建的 ConnectablePublisher 上的 autoconnect()

  • 允许 Publisher 在收到第一个订阅时自动连接。
  • 允许自动取消订阅。
  • 将 Publisher 转换为 Future
  • 为后续订阅者保存结果。

share()multicast(_:) 等其他 Publisher 的 Future

  • 它是一个无状态的 struct Publisher。
  • 只发出错误,从不发出值。
  • 仅在收到订阅后才执行。
  • 它是一个类,在创建时立即执行工作并将结果保存给所有订阅者。

快速概览

  • Publishers 在 Combine 中大多是 struct → 每次订阅都会重新执行 pipeline。
  • share() 创建对上游的唯一订阅并共享它。
  • multicast(_:) 需要一个 Subject,并且由于是 connectable,需要调用 connect()(或 autoconnect())。
  • Future 是一个类,会立即执行其工作并将结果保存,以供后续任何订阅者使用。

关于 share()multicast(_:)autoconnect() 的问答

问题回答
share() 在 Publisher 中的作用是什么?✅ 为原始 Publisher 创建唯一的订阅,并在多个订阅者之间共享其结果。
使用 share() 的共享 Publisher 是否在流已经结束后才进行?✅ 不会接收任何值,只会收到结束事件。
share()✅ 用于 multicast(_:)PassthroughSubject
multicast(_:)share() 有何区别?multicast(_:) 返回一个 ConnectablePublisher,需要调用 connect() 来启动。
在使用 multicast(_:) 创建的 ConnectablePublisher 上,autoconnect() 的作用是什么?✅ 使 Publisher 在收到第一个订阅时自动连接。
share()multicast(_:) 等 Publisher 的 Future 是什么?✅ 是一种在创建时立即执行任务并将结果保存给所有订阅者的类。

快速概览(重复)

  • share()单一内部订阅者,在内部共享值,仅向外部订阅者发送结束事件。
  • multicast(_:) → 返回一个 ConnectablePublisher;需要 connect()(或 autoconnect())来启动传输。
  • autoconnect() → 在收到 第一个订阅 时自动连接。
  • Future → 立即执行任务,并 保存 结果供后续任何订阅者使用。
Back to Blog

相关文章

阅读更多 »

Combine #6:时间操控操作员

时间延迟 delayfor:tolerance:scheduler:options: https://developer.apple.com/documentation/combine/publisher/delayfor:tolerance:scheduler:optio...

Swift Combine 中的热与冷发布者

什么是 Hot 和 Cold Publisher? Cold Publisher Cold Publisher 为每个订阅者创建一个新的执行。当你订阅时,工作会重新开始。swift...

Swift #28:Foundation

抱歉,我没有看到需要翻译的具体文字内容。请提供要翻译的摘录或摘要,我会帮您翻译成简体中文。