【问题标题】:Can Combine be used in struct (instead of class)?可以在结构(而不​​是类)中使用组合吗?
【发布时间】:2020-11-14 05:02:25
【问题描述】:

如下使用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 中声明:

AnyCancellable 实例在取消初始化时会自动调用 cancel()。

鉴于我们不需要在 deinit 期间释放,Combine 是否可以在 struct 中使用而不是 class

【问题讨论】:

  • struct 中使用Combine(我假设您的意思是订阅Combine 发布者)没有固有问题。它会取消初始化和所有。但是,如果您尝试在 .sink 中使用 self,则会遇到问题
  • 如果您要像上面的 receiveValue 那样修改 self ,那么不,您不能以这种方式将 combine 与结构一起使用。但是,如果您只是将发布者保留在 scruct 中,但在外部进行修改,则发布没有问题,等等。
  • 谢谢。我假设AnyCancellation 即使在结构中仍然会自动释放,没有deinit,对吧?
  • 我不明白你的问题,不管类的结构如何,一旦AnyCancellable 的所有者消失了,cancellables 也会消失。引用由类或结构(甚至是闭包)持有没有区别。
  • 谢谢@Cristik。我只是想知道AnyCancellable 使用什么机制来确定它可以被释放。如果它使用classdeinit,那么它在struct 中不起作用。如果它使用的其他机制也适用于类和结构,那么是的,它们应该适用于两者。

标签: swift combine


【解决方案1】:

为了直接回答您的问题,AnyCancellable 不依赖于存储在类中来取消自身。像任何引用计数的对象一样,它可以很好地存储在结构中,并且在没有更多引用时会正确地取消初始化并因此取消。

也就是说,你在这里怀疑是正确的。您可能希望像在此处那样将AnyCancellable 存储在结构中。对于初学者,您必须将 loadItems 函数标记为 mutating 才能编译,因为存储 AnyCancellable 意味着改变 cancellables 数组。

通常,如果您要存储AnyCancellable,那么您将该操作与具有真实身份的事物相关联,因此可以更好地表示为一个类。你基本上是在说“当这个实例消失时取消这个操作”。例如,如果您正在下载要在UIViewController 中显示的图像,您可能希望在UIViewController 因为用户关闭它而消失时取消该下载;也就是说,下载操作与UIViewController特定实例相关联。

由于结构具有值语义,因此将AnyCancellable 与结构的“实例”关联在概念上几乎是不连贯的。结构没有实例,它们只有值。当您将结构作为参数传递给函数时,它会创建一个副本。这意味着如果函数调用loadItems,那么只有函数自己的结构值副本将存储AnyCancellable,并且当函数返回时操作将立即取消,因为您的值的原始副本没有存储@987654334 @。

【讨论】:

    【解决方案2】:

    你不需要deinit也不需要打电话

    cancellables.forEach {
        $0.cancel()
    }
    

    我同意 AnyCancellable 有方法 cancel 很令人困惑,实际上你不需要调用。 当可取消对象被处理时,发布者会被自动取消。 这就是为什么如果忘记将它们存放在某个地方,您将一无所获。

    【讨论】:

    • 谢谢。如果有人想在进程结束之前终止进程,我认为cancel() 仍然存在。我的问题更倾向于cancellable 是否使用类的内部deinit 来确定它是否会被终止?如果是,那么会这样。如果我们在struct 中使用它会出现问题,因为struct 没有deinit
    猜你喜欢
    • 2014-06-29
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    相关资源
    最近更新 更多