combine

25 阅读3分钟

combine

cancellable。每一个订阅都会生成一个 AnyCancellable 对象,用于控制订阅的生命周期。通过这个对象,我们可以取消订阅。当这个对象被释放时,订阅也会被取消。

/// 取消订阅

cancellable.cancel()

Combine提供了2个常用的subject: PassthroughSubject 与CurrentValueSubject

PassthroughSubject 透传事件,不会持有最新的Output

CurrentValueSubject: 除了传递事件之外,会持有最新的Output

@published : combine提供了一个Property Wrapper @Pubilshed 可以快速封装一个变量得到一个publisher

特殊的操作符 erasedToAnyPublisher,让我们可以擦除掉具体类型:

通知的声明周期绑定

1    // 存储为属性,与实例生命周期绑定

    private var notificationCancellable: AnyCancellable?

 private func setupNotification() {

        notificationCancellable = NotificationCenter.default

            .publisher(for: .yourNotification, object: nil)

            .sink { [weak self] notification in

                print("Received notification: (notification)")

                self?.handleNotification(notification)

            }

    }

  // 可选:手动取消订阅

    func cancelNotification() {

        notificationCancellable?.cancel()

        notificationCancellable = nil

    }

方案2:

使用 Set(最佳实践)

// 使用集合管理多个订阅

    private var cancellables = Set()

   NotificationCenter.default

            .publisher(for: UIApplication.didBecomeActiveNotification)

            .sink { [weak self] _ in

                self?.appDidBecomeActive()

            }

            .store(in: &cancellables) // 存储到集合中

可以手动取消所有订阅:

  // 手动取消所有订阅

    func cancelAllSubscriptions() {

        cancellables.forEach { $0.cancel() }

        cancellables.removeAll()

    }

Combine 发布者和订阅者涉及到的 Swift 类型

当你在 Swift 中构建管道时,函数链导致该类型被聚合为嵌套的通用类型。 如果你正在创建一个管道,然后想要将该管道作为 API 提供给代码的另一部分,则对于开发人员来说,暴露的属性或函数的类型定义可能异常复杂且毫无用处。

为了说明暴露的类型复杂性,如果你从 PassthroughSubject 创建了一个发布者,例如:

let x = PassthroughSubject<String, Never>()

.flatMap { name in

return Future<String, Error> { promise in

promise(.success(""))

}.catch { _ in

Just("No user found")

}.map { result in

return "(result) foo"

}

}

结果的类型是:

Publishers.FlatMap<Publishers.Map<Publishers.Catch<Future<String, Error>, Just>, String>, PassthroughSubject<String, Never>>

当你想要暴露这个 subject 时,所有这些混合的细节可能会让你感到非常迷惑,使你的代码更难使用。

为了清理该接口,并提供一个好用的 API,可以使用类型擦除类来包装发布者或订阅者。 这样明确隐藏了 Swift 中从链式函数中构建的类型复杂性。

用于为订阅者和发布者暴露简化类型的两个类是:

每个发布者还继承了一种便利的方法 eraseToAnyPublisher(),它返回一个 AnyPublisher 实例。 eraseToAnyPublisher() 的使用非常像操作符,通常作为链式管道中的最后一个元素,以简化返回的类型。

如果你在上述代码的管道末尾添加 .eraseToAnyPublisher():

let x = PassthroughSubject<String, Never>()

.flatMap { name in

return Future<String, Error> { promise in

promise(.success(""))

}.catch { _ in

Just("No user found")

}.map { result in

return "(result) foo"

}

}.eraseToAnyPublisher()

结果的类型将被简化为:

AnyPublisher<String, Never>

同样的技术在闭包内构造较小的管道时将非常有用。 例如,当你想在闭包中给操作符 flatMap 返回一个发布者时,你可以通过明确的声明闭包应返回 AnyPublisher 来获得更简单的类型推断。 可以在模式 有序的异步操作 中找到这样的一个例子。