SwiftUI - ObservedObject 永远不会被释放

Posted

技术标签:

【中文标题】SwiftUI - ObservedObject 永远不会被释放【英文标题】:SwiftUI - ObservedObject is never deallocated 【发布时间】:2021-07-28 13:54:42 【问题描述】:

我的应用程序正在泄漏模型对象,因为对象保留了保留视图本身的闭包。 最好举个例子来说明。 在下面的代码中,ModelContentView 消失后不会被释放。

//
// Content View is an owner of `Model`
// It passes it to `ViewB`
//
// When button is tapped, ContentView executes action
// assigned to Model by the ViewB
//
struct ContentView: View 
    
    @StateObject private var model = Model()
    
    var body: some View 
        VStack 
            Button(action: 
                model.action?()
            ) 
                Text("Tap")
            
            ViewB(model: model)
        
        .frame(width: 100, height: 100)
        .onDisappear 
            print("CONTENT DISAPPEAR")
        
    


struct ViewB: View 
    
    @ObservedObject var model: Model
    
    var body: some View 
        Color.red.frame(width: 20, height: 20)
            .onAppear 
//
// DANGER:
// Assigning this makes a leak and Model is never deallocated.
// This is because the closure is retaining 'self'
// But since it's a struct, how can we break the cycle here?
//
                model.action =  bAction() 
            
    
    
    private func bAction() 
        print("Hey!")
    
    



class Model: ObservableObject 
    
    var action: (() -> Void)?
    
    deinit 
        print("MODEL DEINIT")
    


我不确定为什么这里会出现某种保留周期。 由于 View 是一个结构体,所以在闭包中引用它应该是安全的,对吧?

【问题讨论】:

我会让模型成为可选的和@ObservedObject,而不是状态对象。我认为这会解决你的问题。 【参考方案1】:

Model 不是一个结构,它是一个ObservableObject,它的类型是AnyObject,它是一个Object

你应该在.onAppear的捕获列表中应用weak to

.onAppear  [weak model] 

认为你也可以只捕获模型,以防问题所在

.onAppear  [model] 

【讨论】:

不幸的是,它没有帮助。请注意,问题在于在分配给model.action 的块中捕获self。在onAppear 中捕获模型很好,不会导致泄漏。【参考方案2】:

Ahoy @msmialko,虽然我无法对我所观察到的情况给出太多理由,但希望这将是朝着正确方向迈出的一步。

我决定从等式中删除 SwiftUI 的内存管理,并使用简单的值和引用类型进行测试:

private func doMemoryTest() 
  struct ContentView 
    let model: Model

    func pressButton() 
      model.action?()
    
  

  struct ViewB 
    let model: Model

    func onAppear() 
      model.action = action
      //  [weak model] in
      //   model?.action = action
      // ()
    

    func onDisappear() 
      print("on ViewB's disappear")
      model.action = nil
    

    private func action() 
      print("Hey!")
    
  

  class Model 
    var action: (() -> Void)?
    deinit 
      print("*** DEALLOCATING MODEL")
    
  


  var contentView: ContentView? = .init(model: Model())
  var viewB: ViewB? = .init(model: contentView!.model)

  contentView?.pressButton()

  viewB?.onAppear()
  contentView?.pressButton()

  // viewB?.onDisappear()
  print("Will remove ViewB's reference")
  viewB = nil
  print("Removed ViewB's reference")

  contentView?.pressButton()

  print("Will remove ContentView's reference")
  contentView = nil
  print("Removed ContentView's reference")

当我运行上面的代码时,这是控制台输出(如您所见,没有释放模型):

Hey!
Will remove ViewB's reference
Removed ViewB's reference
Hey!
Will remove ContentView's reference
Removed ContentView's reference

在上面的示例中,我似乎完全控制了 Model 上的引用计数,但是当我检查 Xcode 中的内存图时,我可以确认 Model 正在通过 action.context 保留自己(我不确定那是什么意思):

要以最小的更改修复保留周期,您可能需要考虑使用ViewB.onDisappear 删除模型的操作分配,就像我在示例中所做的那样。当我取消注释 viewB?.onDisappear() 时,我看到了以下控制台输出:

Hey!
on ViewB's disappear
Will remove ViewB's reference
Removed ViewB's reference
Will remove ContentView's reference
*** DEALLOCATING MODEL
Removed ContentView's reference

祝你好运!

【讨论】:

以上是关于SwiftUI - ObservedObject 永远不会被释放的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 尽管使用了 @Published 和 @ObservedObject,但视图不会更新

SwiftUI:如何在 Pageable 中访问 ObservedObject?

SwiftUI - ObservedObject 永远不会被释放

SwiftUI:@ObservedObject 与 @StateObject 子视图性能?

SwiftUI - 如何基于@ObservedObject 为每个列表视图项创建编辑视图

当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive