简体   繁体   English

Escaping 闭包捕获变异的“自我”参数:结构

[英]Escaping closure captures mutating 'self' parameter: struct

I have the following code I'm using with SwiftUI:我有以下与 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
    }
}

However, I'm getting an error:但是,我收到一个错误:

Escaping closure captures mutating 'self' parameter Escaping 闭包捕获变异的“自我”参数

在此处输入图像描述

I understand that changing the struct to a class would solve this issue, but is there any way to actually capture a mutating self in an escaping closure in a struct?我知道将结构更改为 class 可以解决这个问题,但是有没有办法在结构中的 escaping 闭包中实际捕获变异自我?

As you have found, the quick solution is to use a reference type , a class .如您所见,快速解决方案是使用参考类型class But why is this the case?但为什么会这样呢?

Swift structs are value types, so they are immutable. Swift 结构是值类型,因此它们是不可变的。 You can mark a function as mutating to indicate to the compiler that a function mutates the struct, but what does that actually mean?您可以将 function 标记为正在mutating ,以向编译器指示 function 会变异该结构,但这实际上意味着什么?

Consider a simple struct:考虑一个简单的结构:

struct Counter {
   var count

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

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

Now, try and assign an instance of this to a let constant:现在,尝试将 this 的一个实例分配给一个let常量:

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

You will get an error;你会得到一个错误; you need to use a var .你需要使用var

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

What actually happens when you call a mutating func is that a new Counter is created, with the new count and it is assigned to someCounter .当你调用一个mutating func 时,实际发生的是一个新的Counter被创建,新的count被分配给someCounter It is effectively saying someCounter = Counter(someCounter.count+1)它实际上是在说someCounter = Counter(someCounter.count+1)

Now, think what would happen if you could mutate self in an escaping closure - That new Counter is going to be created at some unspecified time in the future, but execution has already moved on.现在,想想如果你可以在 escaping 闭包中改变self会发生什么 - 新的Counter将在未来某个未指定的时间创建,但执行已经继续。 It is too late to update someCounter .更新someCounter为时已晚。

The other advantage of using a class , as you have found, is that you can use ObservableObject , which makes updating your SwiftUI views much easier.正如您所发现的,使用class的另一个优点是您可以使用ObservableObject ,这使得更新 SwiftUI 视图更加容易。

Solution I finished with:我完成的解决方案:

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
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM