Combine Swift 中 connect() 与 autoconnect() 的隐藏力量

发布: (2025年12月28日 GMT+8 02:37)
6 min read
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the article (the content you’d like translated). Could you please paste the article’s body here? Once I have that, I’ll provide a Simplified Chinese translation while keeping the source link and all formatting exactly as you requested.

介绍

当你订阅一个 Combine 发布者时,你可能会期待值立即开始流动。然而,某些发布者会等待明确的信号才开始工作。可连接的发布者为你提供了这种控制,而 connect()autoconnect() 是启动它们的两种方式。

connect() – 手动控制

一个 可连接的发布者 在你调用其 connect() 方法之前什么也不做。这让你可以:

  • 在发出任何值之前设置多个订阅者。
  • 将数据流的启动与其他应用事件协调。
  • 在准备好之前避免昂贵的工作。

示例

import Combine

let subject = PassthroughSubject<Int, Never>()

// 创建一个可连接的发布者
let connectable = subject
    .print("Debug")
    .makeConnectable()

// 首先订阅
let subscription1 = connectable
    .sink { value in
        print("Subscriber 1 received: \(value)")
    }

let subscription2 = connectable
    .sink { value in
        print("Subscriber 2 received: \(value)")
    }

// 此时什么也不会发生,尽管我们已经有了订阅者

// 手动启动发布者
let connection = connectable.connect()

// 现在值会流向所有订阅者
subject.send(1)
subject.send(2)

关键点

  • 手动启动 – 你决定发布者何时开始。
  • 多个订阅者 – 一旦连接,所有订阅者都会收到值。
  • 可取消connection 可以被取消以停止数据流。

何时使用 connect()

  • 需要等到多个订阅者都准备好时。
  • 想要将启动时机与其他事件同步。
  • 上游工作代价高昂,且只应执行一次。

实际场景:共享网络请求

let url = URL(string: "https://example.com/user")!

let publisher = URLSession.shared
    .dataTaskPublisher(for: url)
    .map(\.data)
    .decode(type: User.self, decoder: JSONDecoder())
    .share()
    .makeConnectable()

// 设置多个订阅者
let sub1 = publisher.sink(
    receiveCompletion: { _ in },
    receiveValue: { user in updateUI(user) }
)

let sub2 = publisher.sink(
    receiveCompletion: { _ in },
    receiveValue: { user in saveToCache(user) }
)

// 只会执行一次网络请求
let connection = publisher.connect()

autoconnect() – 自动启动

autoconnect() 消除了手动调用 connect() 的需求。发布者会在出现 第一个 订阅者时自动连接。

示例

let timer = Timer.publish(every: 1.0, on: .main, in: .common)
    .autoconnect()

let subscription = timer.sink { date in
    print("Timer fired at: \(date)")
}

关键点

  • 立即启动 – 发布者在获得第一个订阅者后立即开始。
  • 方便 – 不需要单独保存连接。
  • 计时器常用 – 适用于周期性事件。

何时使用 autoconnect()

  • 你希望发布者在订阅后立即启动。
  • 正在处理计时器或其他周期性来源。
  • 不需要对多个订阅者进行精确协调。

ViewModel 示例

class ViewModel: ObservableObject {
    @Published var seconds = 0
    private var cancellables = Set<AnyCancellable>()

    func startTimer() {
        Timer.publish(every: 1.0, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.seconds += 1
            }
            .store(in: &cancellables)
    }
}

connect()autoconnect() 之间的选择

需求使用 connect()使用 autoconnect()
需要等待多个订阅者
订阅时立即启动
计时器或周期性事件❌(可能)
昂贵的共享操作✅(如果立即启动没问题)
简单用例
与其他事件的协调

共享单个订阅

通常,你会将 share()(它会创建一个可连接的发布者)与 connect()autoconnect() 结合使用,以确保多个下游订阅者共享同一上游工作。

let shared = expensivePublisher
    .share()          // 使发布者可连接
    .autoconnect()    // 自动启动(或手动使用 .connect())

let sub1 = shared.sink { print("A: \($0)") }
let sub2 = shared.sink { print("B: \($0)") }

存储连接和订阅

请记得保留对连接和可取消对象的引用,否则它们会被释放,导致发布者停止工作。

class DataManager {
    private var connection: Cancellable?
    private var subscriptions = Set<AnyCancellable>()

    func setupConnectable() {
        let connectable = publisher.makeConnectable()

        connectable
            .sink { value in print(value) }
            .store(in: &subscriptions)

        // 将连接保存下来,以便以后可以取消
        connection = connectable.connect()
    }

    deinit {
        connection?.cancel()
    }
}

结论

connect()autoconnect() 为您提供对 Combine 流何时开始发出值的精确控制。当您需要明确的时机和协调时使用 connect(),而在您更倾向于简洁和立即启动时使用 autoconnect()——尤其是计时器或其他周期性发布者。了解这些工具可以让您在 Swift 应用中更有效地管理资源使用和数据流。

Back to Blog

相关文章

阅读更多 »

Combine #13:资源管理

share 和 multicast_: share 大多数 Combine 的 Publisher 是 struct,只是描述一个 pipeline,而不保存共享状态。不会创建一个 i...

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

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