能否在结构体(而不是类)中使用Combine?

3
使用下面的代码时,要使用Combine
var cancellables: [AnyCancellable] = []

func loadItems(tuple : (name : String, imageURL : URL)) {
    URLSession.shared.dataTaskPublisher(for: tuple.imageURL)
        .sink(
            receiveCompletion: {
                completion in
                switch completion {
                case .finished:
                    break
                case .failure( _):
                    return
                }},
            receiveValue: { data, _ in DispatchQueue.main.async { [weak self] in self?.displayFlag(data: data, title: tuple.name) } })
        .store(in: &cancellables)
}

我们不需要像下面这样在deinit中调用cancel

deinit {
    cancellables.forEach {
        $0.cancel()
    }
}

考虑到在 https://developer.apple.com/documentation/combine/anycancellable 中已经说明:

当取消 Cancel 时,AnyCancellable 实例会自动调用 cancel()。

既然我们不需要在 deinit 中进行释放,那么 Combine 是否可以在 struct 中使用,而不是 class


1
struct 中使用 Combine(我假设你的意思是订阅 Combine 发布者)并没有固有的问题。它会被解除初始化。但是,如果你尝试在 .sink 中使用 self,就会遇到问题。 - New Dev
1
如果你像在receiveValue中那样修改self,那么不,你不能以这种方式使用combine和structs。但是,如果你只是在结构体中持有publisher,但在外部修改其他内容,那么释放等方面就没有问题了。 - Asperi
谢谢。我假设即使在结构体中,AnyCancellation 仍然会自动释放,没有 deinit,对吗? - Elye
1
我不理解你的问题。无论类或结构体的结构如何,一旦 AnyCancellable 的所有者消失,可取消订阅也将随之消失。它不会因为引用是由类、结构体(甚至闭包)持有而有所区别。 - Cristik
感谢@Cristik。我只是想知道AnyCancellable使用了什么机制来确定它可以被释放。如果它使用classdeinit,那么它在struct中就不起作用。如果它使用其他机制,也适用于类和结构体,那么它们应该对两者都起作用。 - Elye
AnyCancellable是一个类,因此它具有一个析构器。 - Cristik
2个回答

9
直接回答你的问题,AnyCancellable不需要在类中存储自身来取消操作。就像任何引用计数的对象一样,它可以被存储在结构体中,并且在没有更多对它的引用时会被适当地销毁,从而被取消。
不过,你的怀疑是正确的。你可能不想像这里一样将AnyCancellable存储在结构体中。首先,你必须将loadItems函数标记为mutating才能使其编译通过,因为存储AnyCancellable意味着要改变cancellables数组。
通常情况下,如果你正在存储AnyCancellable,那么你是将该操作与拥有真实身份的某个东西关联起来,因此最好表示为一个类。你基本上是在说“当这个实例消失时取消此操作”。例如,如果你正在下载要在UIViewController中显示的图像,那么如果用户关闭了该视图控制器,你可能希望取消该下载操作;也就是说,下载操作与UIViewController的一个特定实例相关联。
由于结构体具有值语义,将AnyCancellable与结构体的“实例”关联几乎是概念上不一致的。结构体没有实例,它们只有值。当你将结构体作为参数传递给函数时,它会创建一个副本。这意味着如果函数调用了loadItems,那么只有该函数自己的结构体副本才会存储AnyCancellable,并且当函数返回时操作会立即被取消,因为你原始值的副本没有存储AnyCancellable

谢谢你的概述和建议。然而,我还是想知道,将AnyCancellable?值存储到struct的静态变量中怎么样?即使重新创建订阅,可取消引用将被存储并在所有值之间共享。这是为了通过其他静态方法从结构值触发一些分析事件。但是,这样做是否会导致强引用循环?或者呢? - undefined

0

你不需要使用 deinit,也不需要调用它。

cancellables.forEach {
    $0.cancel()
}

我同意AnyCancellable有一个名为cancel的方法,这实际上是不需要调用的,这让人感到相当困惑。 当可取消对象被处理时,发布者会自动取消。 这就是为什么如果忘记在某个地方存储它们,你将什么也收不到。


谢谢。我认为cancel()在事件中仍然存在,如果有人想在进程结束之前终止它。我的问题更多地是关于cancellable是否使用类的内部deinit来确定它是否将被终止?如果是这样,那么如果我们在struct中使用它,会有问题吗?因为struct没有deinit - Elye

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接