转义闭包捕获变异 'self' 参数:struct

Escaping closure captures mutating 'self' parameter: struct

我有以下与 SwiftUI 一起使用的代码:

import Foundation

public struct Trigger {
    public var value = false

    public mutating func toggle() {
        value = true
        let responseDate = Date().advanced(by: 3)

        OperationQueue.main.schedule(after: .init(responseDate)) {
            moveBack()
        }
    }

    private mutating func moveBack() {
        value = false
    }
}

但是,我遇到了一个错误:

Escaping closure captures mutating 'self' parameter

我知道将结构更改为 class 可以解决这个问题,但是有什么方法可以在结构的转义闭包中实际捕获变异的自我吗?

我完成的解决方案:

import Foundation
import Combine

public final class IntervalTrigger: ObservableObject {
    private let timeInterval: TimeInterval
    @Published var value = false

    public init(_ timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }

    public func toggle() {
        value = true
        let responseDate = Date().advanced(by: timeInterval)
        OperationQueue.main.schedule(after: .init(responseDate)) { [weak self] in
            self?.value = false
        }
    }
}

如您所见,快速解决方案是使用 引用类型 class。但是为什么会这样呢?

Swift 结构是值类型,所以它们是不可变的。您可以将函数标记为 mutating 以向编译器指示函数会改变结构,但这实际上意味着什么?

考虑一个简单的结构:

struct Counter {
   var count

   init(_ count: Int = 0)
   {
       self.count = count
   }

   mutating func increment() {
       self.count+=1
   }
}

现在,尝试将其实例分配给 let 常量:

let someCounter = Counter()
someCounter.increment()
print(someCounter.count)

你会得到一个错误;你需要使用 var.

var someCounter = Counter()
someCounter.increment()
print(someCounter.count)

当你调用一个 mutating 函数时实际发生的是一个 new Counter 被创建,新的 count 和它分配给 someCounter。它实际上是在说 someCounter = Counter(someCounter.count+1)

现在,想想如果你可以在转义闭包中改变 self 会发生什么 - 新的 Counter 将在未来某个未指定的时间创建,但执行已经移动在。来不及更新了 someCounter.

正如您所发现的,使用 class 的另一个优点是您可以使用 ObservableObject,这使得更新 SwiftUI 视图变得更加容易.