我的应用程序正在泄漏模型对象,因为对象正在保留保留视图本身的闭包。 最好通过例子来说明。 在下面的代码中,在
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 是一个结构体,因此在闭包中引用它应该是安全的,对吧?
啊@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
祝你好运!
Model
不是一个结构体,它是一个ObservableObject
,其类型为AnyObject
,它是一个Object
你应该在.onAppear的捕获列表中应用weak to
.onAppear { [weak model] }
我认为你也可以捕获模型,以防问题本身出现
.onAppear { [model] }
我也遇到过同样的问题。我想从我的
View
打开 ObservableObject
上的 URL。看起来当你将 View
的方法传递到 ObservableObject
的闭包中时,你实际上创建了循环引用(说实话,我不知道为什么,因为 View
是结构体,但它必须是一些内部 SwiftUI 机制)。
我为解决问题所做的:我用
PassthroughSubject
替换了闭包,它使用 ObservableObject
将值从 View
发送到我的 onReceive(_:perform:)
。
在您的特定情况下,它可能看起来像这样
class Model: ObservableObject {
let action = PassthroughSubject<Void, Never>()
deinit {
print("MODEL DEINIT")
}
func triggerAction() {
action.send()
}
}
struct ViewB: View {
@ObservedObject var model: Model
var body: some View {
Color.red.frame(width: 20, height: 20)
.onReceive(model.action) { bAction() }
}
private func bAction() {
print("Hey!")
}
}