Swift 不应该发生的快速内存冲突

Swift 不应该发生的快速内存冲突,swift,swiftui,Swift,Swiftui,我正在从事一个SwiftUI项目,在那里我使用MVVM架构 从SwiftUI视图更改视图模型对象属性时,会导致视图模型对象中的内存冲突崩溃 错误类型为:同时访问0x600003591b48,但修改需要独占访问 在步骤中,会发生以下情况: 视图模型属性已从视图更改 查看模型属性更改模型属性 模型属性通知有关更改的信息 视图模型接收更改通知 视图模型访问模型对象 内存冲突导致崩溃 相关代码片段如下所示。Xcode项目是一个标准的SwiftUI项目 首先单击“添加”按钮,然后单击“修改”按钮后,将发生

我正在从事一个SwiftUI项目,在那里我使用MVVM架构

从SwiftUI视图更改视图模型对象属性时,会导致视图模型对象中的内存冲突崩溃

错误类型为:同时访问0x600003591b48,但修改需要独占访问

在步骤中,会发生以下情况:

  • 视图模型属性已从视图更改
  • 查看模型属性更改模型属性
  • 模型属性通知有关更改的信息
  • 视图模型接收更改通知
  • 视图模型访问模型对象
  • 内存冲突导致崩溃
  • 相关代码片段如下所示。Xcode项目是一个标准的SwiftUI项目

    首先单击“添加”按钮,然后单击“修改”按钮后,将发生错误

    如果将“更新”代码移到“receiveValue”闭包中,则不会发生错误。同样,如果视图模型类被设置为非泛型,则不会发生错误

    据我所知,代码是正确的,所以我怀疑这是编译器的问题。但我不确定

    import Foundation
    import SwiftUI
    import Combine
    
    struct ContentView: View {
        
        @ObservedObject var item: ViewModel<Model> = ViewModel<Model>()
    
        var body: some View {
            
            VStack {
                Button("Add", action: { item.add(model:Model()) })
                Button("Modify", action: { item.selected.toggle() })
            }
        }
    }
    
    protocol ModelType {
        
        var objectDidChange: ObservableObjectPublisher { get }
        
        var selected: Bool { get set }
    }
    
    class Model: ModelType {
        
        let objectDidChange = ObservableObjectPublisher()
        
        var selected = false {
            didSet {
                objectDidChange.send()
            }
        }
    }
    
    class ViewModel<Model:ModelType>: ObservableObject {
        
        var selected = false {
            didSet {
                model.selected = selected
            }
        }
        
        func add(model: Model) {
            self.model = model
            cancellable = model.objectDidChange.sink(receiveValue: { _ in
                self.update()
            })
        }
        
        private var model: Model! = nil
        private var cancellable: AnyCancellable? = nil
        
        func update() {
            
            // Crash log: Simultaneous accesses to 0x600003591b48, but modification requires exclusive access.
            
            print("update \(model.selected)")
        }
    }
    
    
    <代码>导入基础 导入快捷键 进口联合收割机 结构ContentView:View{ @ObservedObject变量项:ViewModel=ViewModel() var body:一些观点{ VStack{ 按钮(“添加”,操作:{item.Add(model:model())}) 按钮(“修改”,操作:{item.selected.toggle()}) } } } 协议模型类型{ var objectDidChange:observeObjectPublisher{get} 选择的变量:Bool{get set} } 类模型:ModelType{ 让objectDidChange=observeObjectPublisher() 所选变量=false{ 迪塞特{ objectDidChange.send() } } } 类ViewModel:ObservableObject{ 所选变量=false{ 迪塞特{ model.selected=已选择 } } func添加(模型:模型){ self.model=model Cancelable=model.objectDidChange.sink(receiveValue:{in self.update() }) } 私有风险值模型:模型!=nil 私有var可取消:anyCancelable?=nil func update(){ //崩溃日志:同时访问0x600003591b48,但修改需要独占访问。 打印(“更新\(所选型号)”) } }
    简短版本:对于
    型号
    ,需要
    任何对象

    长版本:

    你试图从<代码> >模型> <代码>中读取,而你正处于“代码>自我>模型< /代码>的中间。当您说“如果将“更新”代码移到“receiveValue”闭包中,则不会发生错误”时,这是不完全正确的。我想你的意思是你写了这个:

        cancellable = model.objectDidChange.sink(receiveValue: { _ in
            print("update \(model.selected)")
        })
    
    这很有效,但这是完全不同的代码
    model
    在本例中是局部变量,而不是属性
    self.model
    。如果您这样写,您将得到相同的崩溃:

        cancellable = model.objectDidChange.sink(receiveValue: { _ in
            print("update \(self.model.selected)")
        })
    
    让您来到这里的路径是:

    • ViewModel.selected.didSet

    • 写信给Model.selected为什么不使用ObserveObject协议(和@Published)?好问题?是的。作为简化的一部分,我将其删除,以查看它是否会改变有关错误的某些内容。它没有改变任何东西,我也没有把原来的代码放回去;这就是我删除评论的原因。我想出来了。我正在写,谢谢你的回答。我怀疑这就是问题所在,首先,我尝试像这样约束泛型。我是这样做的:class ViewModel:observeObject不幸的是,这并没有产生与直接在协议上设置约束相同的效果。我将状态更改为unanswered,因为我仍然很难找到适合我的解决方案。问题是让协议从任何对象继承只能部分解决问题。如果添加更多继承,问题仍然存在。例如,如果协议是这样构造的:
      protocolmodeltype:AnyObject、ProtocolA、ProtocolB
      我就无法重现后一个示例(ModelType需要AnyObject和其他两个协议)。您需要发布代码来演示这一点。(我可以复制
      模型:AnyObject&ModelType
      崩溃,我建议你为它打开一个Swift bug。尽管我有点怀疑需要这么多的协议和层,但这应该会起作用;你的设计可能过于复杂了。即使如此,它也应该会起作用。)如果将“objectDidChange”发布者放入ProtocolA,而“selected”布尔属性,加上ProtocolB中加入了一个新的description()函数,就会发生崩溃。然而,我注意到它不会崩溃的组合。最后,我无法避免在我的原始项目中有很多协议,因此,我现在使用一种黑客方法,防止视图模型属性导致回调到视图模型对象。我认为这一切都是一个应该报告的bug,希望有一天能够解决(在我最初的项目中,约束协议继承了7-9个次要协议)。我再次改变了主意。原始帖子没有将泛型约束为类,因此答案是正确的。此外,我将在Swift中以错误的形式报告该问题。
          print("update \(model!)")
      
      protocol ModelType: AnyObject { ... }