Combine 学习之 Subscriber

6 阅读2分钟

在 Combine 响应式编程框架中,Subscriber(订阅者)  是数据流的终点,负责接收 Publisher(发布者)发送的值、完成事件和错误信息,并执行最终处理逻辑Apple Developer Documentation。它与 Publisher 通过Subscription(订阅)  建立连接,实现背压(Backpressure)  控制,让订阅者能够控制接收数据的速度

public protocol Subscriber<Input, Failure> : CustomCombineIdentifierConvertible { 
  订阅者期望接收的值类型 
    associatedtype Input 
  订阅者可能接收的错误类型(永不失败用Never) 
    associatedtype Failure : Error 
   
  1. 订阅成功时调用,接收Subscription对象 
    func receive(subscription: Subscription) 
  2. 接收单个值,返回Demand表示还能接收多少值 
    func receive(_ input: Input) -> Subscribers.Demand 
  3. 接收完成事件(正常完成或错误终止) 
    func receive(completion: Subscribers.Completion<Failure>) }
    
    

Demand是 Combine 的核心特性,实现背压控制,让订阅者决定接收数据的节奏

 public enum Demand : Equatable, CustomStringConvertible { 
   /// 不接收任何数据 
   case none
   /// 接收n个数据 
   case ax(Int)
   /// 制接收数据(常用)  
   case mited 
 }
 

Demand 规则

  • 初始请求:必须在receive(subscription:)中调用subscription.request(_:),否则不会收到任何数据

  • 累积效应:Demand 是累积的,例如先请求.max(2),再返回.max(3),总共可接收 5 个数据

  • 自动调整receive(_:)返回的 Demand 会更新订阅者的剩余需求

  • 完成终止:收到完成事件后,所有未处理的 Demand 自动失效

内置 Subscriber 类型

  • sink 通过闭包处理值和完成事件,返回AnyCancellable用于取消订阅

    var cancellables = Set<AnyCancellable>() 
    // 处理值和完成/错误 
    [1,2,3].publisher.sink { ompletion in 
    switch completion { 
        case .finished: 
        print("完成")
        case .failure(let error): 
        print("错误: \(error)") 
        } 
    } eceiveValue: { value in 
         print("收到值: \(value)") 
    }.store(in: &cancellables)
    
  • Assign(属性绑定)直接将值绑定到对象的属性,适合 UI 更新场景

    class UserViewModel {
         @Published var username: String = "" 
         var cancellables = Set<AnyCancellable>()
    }
    let vm = UserViewModel() 
    ["Alice""Bob""Charlie"].publisher
                               .assign(to: \.username, on: vm)
                               .store(in:&vm.cancellables) 
    // 监听属性变化 
    vm.$username .sink { 
      print("用户名更新: \($0)") 
    }.tore(in: &vm.cancellables)
    
  • AnySubscriber(类型擦除)包装任意 Subscriber,隐藏具体类型,适用于需要抽象订阅者的场景

     let anySubscriber = AnySubscriber<StringNever>(receiveSubscription: { 
        subscription insubscription.request(.unlimited) 
     }, receiveValue: { value -> Subscribers.Demand in 
         print("AnySubscriber 收到: \(value)"return .none 
     }, receiveCompletion: { completion in 
         print("AnySubscriber 完成: \(completion)") 
     })
     Just("测试AnySubscriber").subscribe(anySubscriber)
     
    
  • 自定义 Subscriber 实现

    class SumSubscriber: Subscriber { 
      // 定义接收的数据类型和错误类型 
      typealias Input = Int 
      typealias Failure = Never 
      private var total: Int = 0 
      private var subscription: Subscription? 
      1. 订阅成功时调用 
      func receive(subscription: Subscription) { 
        self.subscription = subscription 
        // 请求所有数据      
        subscription.request(.unlimited) 
      } 
      // 2. 接收单个值 
      func receive(_ input: Int) -> Subscribers.Demand { 
        total += input 
        print("当前总和: \(total)") 
        // 继续接收所有数据 return.unlimited 
      } 
      // 3. 接收完成事件 
      func receive(completion: Subscribers.Completion<Never>) { 
        print("最终总和: \(total)") 
        print("完成状态: \(completion)") 
      // 清理资源 subscription = nil 
      } 
    } 
    // 使用自定义订阅者 
    let sumSubscriber = SumSubscriber() 
    [1, 2, 3, 4, 5].publisher.subscribe(sumSubscriber)
    
  • Subscription 管理与取消

    取消订阅的三种方式

    1. 自动取消AnyCancellable被销毁时自动调用cancel()

    2. 手动取消:调用AnyCancellablecancel()方法

    3. 集合管理:使用Set<AnyCancellable>统一管理多个订阅

      var cancellables = Set<AnyCancellable>()
      let cancellable = Timer.publish(every: 1, on: .main, in: .common)
      .autoconnect()
      .sink { 
         print($0) 
      }
      // 手动取消 cancellable.cancel()
      // 或添加到集合管理 
      cancellable.store(in:&cancellables) 
      // 清空集合取消所有订阅 
      cancellables.removeAll()