Swift Combine — AnyCancellable的理解与使用
在Swift的Combine框架中,AnyCancellable是一个非常重要的类型,用于持有和管理订阅关系。它可以持有任何类型的Cancellable对象,并提供了一种类型擦除的方式来管理订阅关系。
首先看一下AnyCancellable的定义:
final public class AnyCancellable : Cancellable, Hashable {
final public func cancel()
}
public protocol Cancellable {
func cancel()
}
AnyCancellable是一个class类型,继承了Cancellable协议并实现了cancel方法。cancel方式是用来取消订阅的,也就是断开Publisher和Subscriber的联系,从而不再接受Publisher的任何事件。
另外特别要强调的是AnyCancellable实例在deinit时(即销毁时)时自动调用cancel()。
sink 和 assign 的返回值都是 AnyCancellable,所以它们都可以调用 cancel 方法来取消订阅。当 AnyCancellable 所在的类执行析构函数时,AnyCancellable 的析构函数也会被触发。
class AnyCancellableViewModel: ObservableObject {
let publisher = PassthroughSubject<String, Never>()
var subScription: AnyCancellable?
init() {
setUpSubscription()
}
func setUpSubscription() {
subScription = publisher
.sink { value in
print("Received value: \(value)")
}
}
func sendMessage() {
publisher.send("text")
}
func cancelSubscription() {
print("Subscription cancelled")
subScription?.cancel()
}
}
在上面代码中,在AnyCancellableViewModel类中定义了一个PassthroughSubject类型的publisher和一个AnyCancellable类型的subScription。
在方法setUpSubscription()中,通过sink方法订阅Publisher,并将sink方法返回的对象用subScription属性持有,如果不持有,那么出了方法作用域订阅就失效了,持有的话后期不需要的时候也可以取消订阅,比如在cancelSubscription()方法中,就调用cancel()方法取消订阅。

上面效果中,连续调用send方法,不断地有输出,当调用cancel方法后,再次调用send方法,就没有输出了。
完整代码如下:
class AnyCancellableViewModel: ObservableObject {
let publisher = PassthroughSubject<String, Never>()
var subScription: AnyCancellable?
init() {
setUpSubscription()
}
func setUpSubscription() {
subScription = publisher
.sink { value in
print("Received value: \(value)")
}
}
func sendMessage() {
publisher.send("text")
}
func cancelSubscription() {
print("Subscription cancelled")
subScription?.cancel()
}
}
struct AnyCancellableDemo: View {
@StateObject private var viewModel = AnyCancellableViewModel()
var body: some View {
VStack {
Button("Send text") {
viewModel.sendMessage()
}
Button("Cancel subscription") {
viewModel.cancelSubscription()
}
}
.buttonStyle(BorderedProminentButtonStyle())
}
}
viewModel为界面持有,当界面销毁的时候,viewModel也就销毁了,继而AnyCancellable实例对象也就销毁了,销毁时自动调用cancel方法,取消订阅。
在cancelSubscription()方法中,如果直接将subScription设置为nil,同样有调用cancel方法的效果。
当有一个Publisher的时候,可以用一个AnyCancellable实例对象去持有订阅,那么如果有多个Publisher的时候,定义很多个AnyCancellable实例对象有点说不过去了,这时候就要用到AnyCancellable的下面这个方法了。
extension AnyCancellable {
final public func store(in set: inout Set<AnyCancellable>)
}
store(in:)方法接受一个AnyCancellable类型的集合,而这个集合就是用来存储订阅的,下面修改一下上面的代码。
class AnyCancellableViewModel: ObservableObject {
let publisher = PassthroughSubject<String, Never>()
// var subScription: AnyCancellable?
private var cancellable = Set<AnyCancellable>() // 1
init() {
setUpSubscription()
}
func setUpSubscription() {
// 2
publisher
.sink { value in
print("Received value: \(value)")
}
.store(in: &cancellable)
}
func sendMessage() {
publisher.send("text")
}
func cancelSubscription() {
print("Subscription cancelled")
// subScription?.cancel()
// 3
cancellable.removeAll()
}
}
首先定义一个AnyCancellable类型的集合cancellable,然后在sink方法订阅的时候调用store(in:)方法,将订阅存储到cancellable集合中,最后如果取消订阅的话,调用cancellable.removeAll()方法。
这样如果有两个或者更多个Publisher都是可以存到cancellable集合中。
总之 AnyCancellable是一种管理订阅状态的工具,能根据开发者需要在某个时段切断 Publisher和 Subscriber的联系。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。
