iOS Combine自定义EvenSubject无法发射值问题排查
问题分析与修复
核心问题
你的EvenSubject无法发射值的原因有两个:
- 订阅时机错误:你先调用
send发送值,之后才通过sink订阅。底层的PassthroughSubject是无缓存的“热”Subject,只会传递订阅之后发送的值,之前的所有值都会丢失。 - 底层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
相关产品推荐
相关产品推荐

