在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
的联系。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。