Combine 预取与缓存结合:如何实现无订阅不重启发布者直接返回缓存
问题根因
- 你当前使用
multicast + autoconnect的实现,问题出在autoconnect()的默认逻辑:它会跟踪订阅者数量,当最后一个订阅者取消订阅(数量降到0)时,自动取消对上游发布者的订阅;当新订阅者到来(数量升到1)时,会重新连接上游、触发全量数据拉取。 - 即使你的自定义
ShareReplySubject保留了历史缓存,上游重新连接后推送的新事件会覆盖原有缓存,不符合「无订阅时重启发布者直接返回旧缓存」的预期。
核心调整思路
要满足需求,需要修改连接管理逻辑:
- 仅在第一个订阅者到来时触发一次上游连接,连接完成后永久保持上游订阅,不会因为订阅者数量降到0而断开
- 长期持有带缓存的
ShareReplySubject实例,保证无订阅时缓存不会被释放 - 新订阅者到来时直接从缓存的Subject获取数据,不触发上游重新连接
具体实现代码
首先需要一个线程安全的共享状态管理类,持有Subject和上游连接句柄:
private final class ShareReplayState<Output, Failure: Error> { /// 带数据缓存的Subject,长期持有不被释放 let subject: ShareReplySubject<Output, Failure> /// 上游连接的Cancellable,一旦建立就不会主动取消 private var upstreamConnection: Cancellable? /// 保护状态的锁,避免多订阅者同时触发多次连接 private let lock = NSLock() init(bufferSize: Int) { self.subject = ShareReplySubject(bufferSize: bufferSize) } /// 线程安全的获取/创建上游连接的方法 func getOrCreateConnection(_ connectionBuilder: () -> Cancellable) -> Cancellable? { lock.lock() defer { lock.unlock() } if let existingConn = upstreamConnection { return existingConn } let conn = connectionBuilder() upstreamConnection = conn return conn } }
然后修改shareReplay的实现,移除autoconnect(),手动管理连接生命周期:
extension Publisher { func shareReplay(_ bufferSize: Int = 1) -> AnyPublisher<Output, Failure> { /// 持有共享状态,只要返回的AnyPublisher不被释放,缓存和上游连接就会存活 let state = ShareReplayState<Output, Failure>(bufferSize: bufferSize) /// 用Deferred包装订阅逻辑,只有当有订阅者时才执行内部判断 return Deferred { // 线程安全的检查是否已经建立上游连接,未建立则发起一次永久连接 state.getOrCreateConnection { [weak self] in guard let self = self else { return AnyCancellable {} } return self.multicast(subject: state.subject).connect() } // 直接返回带缓存的Subject给新订阅者 return state.subject } .eraseToAnyPublisher() } }
注意事项
- 你需要把返回的
AnyPublisher(即示例中的products)长期持有在内存中(比如存放在ViewModel、单例的属性中),否则对应的共享状态、缓存、上游连接都会被释放,无法实现永久缓存的效果。 - 该实现只会触发一次上游数据拉取,后续所有订阅者都会直接复用缓存结果,不会重复发起网络请求、数据库查询等冷发布者任务。
- 如果你兼容iOS 17及以上版本,可以直接使用原生提供的实现,无需自定义:
原生的products = getProducts.shareReplay( bufferSize: 1, scope: .forever ).forever作用域完全符合你的需求,会自动管理上游连接、缓存,不会因订阅者数量变化重启上游。
内容的提问来源于stack exchange,提问作者harisk92
相关产品推荐
相关产品推荐

