如何取消接收器内的组合订阅?
How do I cancel a combine subscription within a sink?
我的应用程序中的某个功能的架构有些复杂。
示例代码如下。我最初的期望是这只会打印一次,因为我调用了 cancellableSet.removeAll()
。但这实际上最终被调用了两次,这在我的应用程序中造成了问题。
我如何获得它,以便它仅在订阅存储在可取消集中后触发 sink
中的内容。
请注意,我将在此处提及一些限制。我的示例代码只是对此进行了简化。
- 不能使用
take
或 drop
操作,因为这可能会被调用不确定的次数。
import Combine
enum State {
case loggedOut
case doingSomething
}
let aState = CurrentValueSubject<State, Never>(.doingSomething)
private var cancellableSet: Set<AnyCancellable> = []
func logUserOut() {
cancellableSet.removeAll()
aState.send(.loggedOut)
}
func doSomethingElse() { }
aState.sink { newState in
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
.store(in: &cancellableSet)
如果此代码所在的队列 运行 是一个串行队列,那么也许您可以将接收器内代码的执行移动到队列的末尾。这样,程序会找到时间将订阅存储在集合中。
aState.sink { newState in
DispatchQueue.main.async { // or whatever other queue you are running on
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
}
.store(in: &cancellableSet)
虽然有点脏。
您的代码中的问题是订阅在调用 sink
returns 之前开始同步传递值,因此甚至在调用 store
开始之前。
解决这个问题的一种方法是在订阅前将 aState
变成 ConnectablePublisher
。 ConnectablePublisher
在其 connect
方法被调用之前不会发布。所以调用connect
after store
returns.
您可以在 Failure == Never
的任何 Publisher
上使用 makeConnectable
方法将其包装在 ConnectablePublisher
.
中
let connectable = aState.makeConnectable()
connectable.sink { newState in
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
.store(in: &cancellableSet)
connectable.connect()
我的应用程序中的某个功能的架构有些复杂。
示例代码如下。我最初的期望是这只会打印一次,因为我调用了 cancellableSet.removeAll()
。但这实际上最终被调用了两次,这在我的应用程序中造成了问题。
我如何获得它,以便它仅在订阅存储在可取消集中后触发 sink
中的内容。
请注意,我将在此处提及一些限制。我的示例代码只是对此进行了简化。
- 不能使用
take
或drop
操作,因为这可能会被调用不确定的次数。
import Combine
enum State {
case loggedOut
case doingSomething
}
let aState = CurrentValueSubject<State, Never>(.doingSomething)
private var cancellableSet: Set<AnyCancellable> = []
func logUserOut() {
cancellableSet.removeAll()
aState.send(.loggedOut)
}
func doSomethingElse() { }
aState.sink { newState in
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
.store(in: &cancellableSet)
如果此代码所在的队列 运行 是一个串行队列,那么也许您可以将接收器内代码的执行移动到队列的末尾。这样,程序会找到时间将订阅存储在集合中。
aState.sink { newState in
DispatchQueue.main.async { // or whatever other queue you are running on
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
}
.store(in: &cancellableSet)
虽然有点脏。
您的代码中的问题是订阅在调用 sink
returns 之前开始同步传递值,因此甚至在调用 store
开始之前。
解决这个问题的一种方法是在订阅前将 aState
变成 ConnectablePublisher
。 ConnectablePublisher
在其 connect
方法被调用之前不会发布。所以调用connect
after store
returns.
您可以在 Failure == Never
的任何 Publisher
上使用 makeConnectable
方法将其包装在 ConnectablePublisher
.
let connectable = aState.makeConnectable()
connectable.sink { newState in
print("numberOfSubscriptions is: \(cancellableSet.count)")
switch newState {
case .loggedOut:
doSomethingElse()
case .doingSomething:
logUserOut()
}
}
.store(in: &cancellableSet)
connectable.connect()