SwiftUI - ObservedObject 永远不会被释放
Posted
技术标签:
【中文标题】SwiftUI - ObservedObject 永远不会被释放【英文标题】:SwiftUI - ObservedObject is never deallocated 【发布时间】:2021-07-28 13:54:42 【问题描述】:我的应用程序正在泄漏模型对象,因为对象保留了保留视图本身的闭包。
最好举个例子来说明。
在下面的代码中,Model
在ContentView
消失后不会被释放。
//
// 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 子视图性能?