Swift Combine中sink返回.none致ObjectBox订阅自动取消问题
ObjectBox Combine订阅收到首个值后自动取消订阅问题
问题详情
我使用以下代码监听ObjectBox查询:
query.publisher .mapError(DatabaseError.other(nested:)) .eraseToAnyPublisher() .map(mapEntities(_:)) .catch(handleError) // 返回Empty<[..], Never>,也试过Just<[...]> .receive(on: DispatchQueue.main) .sink { results in self.allEntities = results })
- 保留
eraseToAnyPublisher是因为此前代码位于另一个函数中,为简化已移除 - 已保存返回的cancellable
收到首个值后,ObjectBox日志显示Unsubscribe。查看ObjectBox Swift的Combine源码后发现,这种情况会在订阅者返回demand.none时发生,但我无法确定原因。
解决办法
1. 修复catch操作符的终止问题
你用catch返回的Empty或Just都是有限Publisher:前者会立即发送finished事件,后者发送单个值后也会触发finished,这会直接终止整个订阅链(包括ObjectBox的无限查询Publisher)。
如果希望出错时返回默认值并继续监听数据变化,改用replaceError:
query.publisher .mapError(DatabaseError.other(nested:)) .eraseToAnyPublisher() .map(mapEntities(_:)) .replaceError(with: []) // 出错时返回空数组,不中断订阅 .receive(on: DispatchQueue.main) .sink { results in self.allEntities = results })
如果必须保留catch逻辑,要确保返回的是无限Publisher,比如出错后重新订阅原查询:
.catch { _ in query.publisher .mapError(DatabaseError.other(nested:)) .eraseToAnyPublisher() .map(mapEntities(_:)) }
2. 显式处理sink的Completion闭包
默认sink(receiveValue:)会隐式忽略Completion事件,但有时这种隐式处理可能导致订阅者的Demand被误设为.none。显式写出完整的sink闭包,确保订阅者保持.unlimited的Demand:
.sink( receiveCompletion: { _ in }, // 可根据需求处理错误/完成事件 receiveValue: { results in self.allEntities = results } )
3. 排查类型擦除的影响
暂时移除eraseToAnyPublisher测试,确认是否还会出现自动取消的情况。如果移除后恢复正常,说明之前的函数返回的Publisher被错误地标记为有限类型,需要检查上游函数的Publisher定义。
内容的提问来源于stack exchange,提问作者Krisztian
相关产品推荐
相关产品推荐

