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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:45:03