You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Combine自定义EvenSubject无法发射值问题排查

问题分析与修复

核心问题

你的EvenSubject无法发射值的原因有两个:

  1. 订阅时机错误:你先调用send发送值,之后才通过sink订阅。底层的PassthroughSubject是无缓存的“热”Subject,只会传递订阅之后发送的值,之前的所有值都会丢失。
  2. 底层Subject类型不匹配需求:如果需要订阅者能获取订阅前的初始值,PassthroughSubject无法满足,它不会缓存任何值。

修复方案

方案1:调整订阅时机(仅接收订阅后的偶数)

如果只需要订阅之后的偶数被接收,把订阅代码移到所有send调用之前即可:

class EvenSubject<Failure: Error>: Subject {
    
    typealias Output = Int
    
    private let wrapped: PassthroughSubject<Int, Failure>
    
    init(initialValue: Int) {
        self.wrapped = PassthroughSubject()
        let evenInitialValue = EvenSubject.isEven(value: initialValue) ? initialValue : 0
        send(evenInitialValue)
    }
    
    private static func isEven(value: Int) -> Bool {
        value % 2 == 0
    }
    
    func send(_ value: Output) {
        print("send")
        if value % 2 == 0 {
            wrapped.send(value)
        }
    }
    
    func send(completion: Subscribers.Completion<Failure>) {
        print("send completion")
        wrapped.send(completion: completion)
    }
    
    func send(subscription: Subscription) {
        print("send subscription")
        wrapped.send(subscription: subscription)
    }
    
    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        wrapped.receive(subscriber: subscriber)
    }
}

// 先订阅,再发送值
let subject = EvenSubject<Never>(initialValue: 4)

let cancellable = subject.sink { _ in
    
} receiveValue: { value in
    print(value) // 输出:4、10、14
}

subject.send(10)
subject.send(7)
subject.send(14)

方案2:改用CurrentValueSubject作为底层(支持订阅前的初始值)

如果需要订阅者能获取订阅前的初始偶数,将底层PassthroughSubject替换为CurrentValueSubject,确保初始值为偶数:

class EvenSubject<Failure: Error>: Subject {
    
    typealias Output = Int
    
    private let wrapped: CurrentValueSubject<Int, Failure>
    
    init(initialValue: Int) {
        let evenInitialValue = EvenSubject.isEven(value: initialValue) ? initialValue : 0
        self.wrapped = CurrentValueSubject(evenInitialValue)
    }
    
    private static func isEven(value: Int) -> Bool {
        value % 2 == 0
    }
    
    func send(_ value: Output) {
        print("send")
        if value % 2 == 0 {
            wrapped.send(value)
        }
    }
    
    func send(completion: Subscribers.Completion<Failure>) {
        print("send completion")
        wrapped.send(completion: completion)
    }
    
    func send(subscription: Subscription) {
        print("send subscription")
        wrapped.send(subscription: subscription)
    }
    
    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        wrapped.receive(subscriber: subscriber)
    }
    
    // 可选:获取当前最新偶数
    var currentValue: Int {
        wrapped.value
    }
}

let subject = EvenSubject<Never>(initialValue: 4)

subject.send(10)
subject.send(7)
subject.send(14)

let cancellable = subject.sink { _ in
    
} receiveValue: { value in
    print(value) // 输出:14(订阅时先发送最新缓存的偶数,后续send偶数会继续输出)
}

方案3:缓存所有历史偶数(订阅时补发全部历史值)

如果需要订阅者能收到所有发送过的偶数,在EvenSubject中维护缓存数组,订阅时补发所有历史值:

class EvenSubject<Failure: Error>: Subject {
    
    typealias Output = Int
    
    private let wrapped: PassthroughSubject<Int, Failure>
    private var evenValuesCache: [Int] = []
    
    init(initialValue: Int) {
        self.wrapped = PassthroughSubject()
        let evenInitialValue = EvenSubject.isEven(value: initialValue) ? initialValue : 0
        send(evenInitialValue)
    }
    
    private static func isEven(value: Int) -> Bool {
        value % 2 == 0
    }
    
    func send(_ value: Output) {
        print("send")
        if value % 2 == 0 {
            evenValuesCache.append(value)
            wrapped.send(value)
        }
    }
    
    func send(completion: Subscribers.Completion<Failure>) {
        print("send completion")
        wrapped.send(completion: completion)
    }
    
    func send(subscription: Subscription) {
        print("send subscription")
        wrapped.send(subscription: subscription)
    }
    
    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        // 补发所有缓存的偶数
        evenValuesCache.forEach { wrapped.send($0) }
        wrapped.receive(subscriber: subscriber)
    }
}

let subject = EvenSubject<Never>(initialValue: 4)

subject.send(10)
subject.send(7)
subject.send(14)

let cancellable = subject.sink { _ in
    
} receiveValue: { value in
    print(value) // 输出:4、10、14
}

内容的提问来源于stack exchange,提问作者user19037628

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 02:34:59