简体   繁体   English

SwiftUI Escaping 闭包捕获变异的“自我”参数

[英]SwiftUI Escaping closure captures mutating 'self' parameter

I have a view which can be opened in two ways.我有一个可以通过两种方式打开的视图。 One with the data provided to it, and the other with a document reference to a Firestore document.一个带有提供给它的数据,另一个带有对 Firestore 文档的文档引用。 I have created two constructors and in the first one I'm providing the data and in the other one I'm a providing the document reference.我创建了两个构造函数,第一个提供数据,另一个提供文档参考。 Then I'm using this reference to make a network call bit I'm getting an error:然后我使用这个引用进行网络调用位我收到一个错误:

Escaping closure captures mutating 'self' parameter

Any idea on how to solve this problem?关于如何解决这个问题的任何想法?

@State var request: RequestModel?

init(request: RequestModel) {
    self.request = request
}

init(reference: DocumentReference) {
    FirestoreService().fetchDocument(documentReference: reference) { (request: RequestModel) in
        self.request = request
    }
}

An object's initializer cannot do anything asynchronous.一个对象的初始化器不能做任何异步的事情。 Its job is to produce the object immediately, with all its properties initialized.它的工作是立即生成对象,并初始化其所有属性。

I'm sorry to say the above answer was wrong.我很抱歉地说上面的答案是错误的。

An object's initializer cannot do anything asynchronous.一个对象的初始化器不能做任何异步的事情。 Its job is to produce the object immediately, with all its properties initialized它的工作是立即生成对象,并初始化其所有属性

--> This is absolutely wrong. --> 这绝对是错误的。

I've done successfully tons of multi-threaded programming for 10 years in Objective-C/C/Swift: without any memory leakage nor data accesses nor access races. 10 年来,我在 Objective-C/C/Swift 中成功完成了大量多线程编程:没有任何内存泄漏、数据访问或访问竞争。

I've successfully made some objects initialized asynchronously in making my games, esp.我已经成功地在制作我的游戏时异步初始化了一些对象,尤其是。 when I need to make as many objects as being initialized in a performant way along with lots of lazy objects.当我需要创建与以高性能方式初始化的对象以及大量惰性对象一样多的对象时。

initializers can do asynchronous but the problem here is 'self' in the escaping closure of the following can not be modified.初始值设定项可以异步执行,但这里的问题是无法修改以下转义闭包中的“self”。

init(reference: DocumentReference) {
    FirestoreService().fetchDocument(documentReference: reference) {    
        (request: RequestModel) in
         self.request = request
}

The closure in the initializer in detail is初始化器中的闭包详细是

{        
    @escaping [unowned self] (request: RequestModel) in

        self.request = request
}

Therefore, 'self' in the closure will outlive the function.因此,闭包中的 'self' 将比函数更长寿。 And the code above seems that it is part of View type, created as structure.上面的代码似乎是视图类型的一部分,创建为结构。

From https://docs.swift.org/swift-book/LanguageGuide/Closures.html ,https://docs.swift.org/swift-book/LanguageGuide/Closures.html

"If self is an instance of a structure or an enumeration, you can always refer to self implicitly. However, an escaping closure can't capture a mutable reference to self when self is an instance of a structure or an enumeration. Structures and enumerations don't allow shared mutability, as discussed in Structures and Enumerations Are Value Types." “如果 self 是结构或枚举的实例,您总是可以隐式地引用 self。但是,当 self 是结构或枚举的实例时,转义闭包无法捕获对 self 的可变引用。结构和枚举不允许共享可变性,如结构和枚举是值类型中所述。”

Therefore, the 'self' can not be mutable.因此,“自我”不能是可变的。

It is why your code is getting the error messgage:" Escaping closure captures mutating 'self' parameter".这就是为什么您的代码会收到错误消息的原因:“转义闭包捕获了变异的‘self’参数”。

A better solution to this is moving the part to another place inside of the modifier like 'didAppear()'.一个更好的解决方案是将部件移动到修改器内部的另一个位置,如“didAppear()”。

Because the questioner didn't provide more of his or her codes here.因为提问者没有在这里提供更多他或她的代码。 I can't provide a more specific answer here.我无法在这里提供更具体的答案。

This can probably be divided into two different questions:这大概可以分为两个不同的问题:

1: properties of Struct can't be altered in Closures like 'didSet' or 'willSet'. 1:结构的属性不能在像“didSet”或“willSet”这样的闭包中改变。

2: Only Observable struct that has been declared with @ObservedObject or @EnvironmentObject .etc can call UI to update while the properties is altered. 2:只有使用@ObservedObject 或@EnvironmentObject .etc 声明的Observable 结构体才能在属性更改时调用UI 进行更新。 Because such modifier give property special getter and setter functions因为这样的修饰符赋予属性特殊的 getter 和 setter 函数

I was about to post a question regarding exactly this issue.我正要发布一个关于这个问题的问题。 With the answer from @Sungwood Kim, I understand now that I actually try to change the value-type (struct) from within itself while not knowing when this will happen (async).有了@Sungwood Kim 的回答,我现在明白了,我实际上是在尝试从内部更改值类型(结构),但不知道何时会发生(异步)。

So, if you need such a construct like:因此,如果您需要这样的构造,例如:

  1. A struct where its content is read asynchronously (your case "Firestone", mine is "HealthKit")异步读取其内容的struct (您的案例“Firestone”,我的是“HealthKit”)
  2. Within the struct, a mutating function handles the asynchron read and wants to update itself with the received data.在结构中, mutating的 function 处理异步读取,并希望使用接收到的数据更新自己

Then the workaround moves the update to be done to the outside: Move the update code into a closure and call the closure within the reading function.然后解决方法将要完成的更新移到外部:将更新代码移动到闭包中并在读取 function 中调用闭包。

Here's some code:这是一些代码:

struct Value {
    var localData: Int = 0

    mutating func thisOneWorks(_ completion: @escaping () -> Void) {
        DispatchQueue.main.async { completion() }
    }

    // Cannot compile the following function: "Escaping closure captures mutating 'self' parameter"
    mutating func thisOneDoesNotWork(_ value: Int) {
        DispatchQueue.main.async { self.localData = value }
    }
 }

Where completion contains the updating code:其中completion包含更新代码:

    var value = Value()
    value.thisOneWorks { value.localData = 4711 }

Note: The updating code might be big and complex.注意:更新代码可能很大而且很复杂。 You can put it into a synchronously executed, mutating function and have the completion-closure call it.您可以将其放入同步执行的变异 function 并让完成关闭调用它。 With this, the updating code is encapsulated within the struct.有了这个,更新代码被封装在结构中。

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

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