【发布时间】:2021-08-29 21:48:41
【问题描述】:
在使用List 和id: \.self 时,我在SwiftUI 中遇到了奇怪的内存泄漏,其中只有一些项目被破坏。我正在使用 macOS Monterey Beta 5。
复制方法如下:
- 新建一个空白的 SwiftUI macOS 项目
- 粘贴以下代码:
class Model: ObservableObject {
@Published var objs = (1..<100).map { TestObj(text: "\($0)")}
}
class TestObj: Hashable {
let text: String
static var numDestroyed = 0
init(text: String) {
self.text = text
}
static func == (lhs: TestObj, rhs: TestObj) -> Bool {
return lhs.text == rhs.text
}
func hash(into hasher: inout Hasher) {
hasher.combine(text)
}
deinit {
TestObj.numDestroyed += 1
print("Deinit: \(TestObj.numDestroyed)")
}
}
struct ContentView: View {
@StateObject var model = Model()
var body: some View {
NavigationView {
List(model.objs, id: \.self) { obj in
Text(obj.text)
}
Button(action: {
var i = 1
model.objs.removeAll(where: { _ in
i += 1
return i % 2 == 0
})
}) {
Text("Remove half")
}
}
}
}
- 运行应用程序,然后按“删除一半”按钮。一直按住它,直到所有物品都消失。但是,如果您查看控制台,您会看到只有 85 件物品被销毁,而有 99 件物品。 Xcode 内存图也支持这一点。
这似乎是由id: \.self 行引起的。删除它并将其切换为 id: \.text 可以解决问题。
但是我使用id: \.self 的原因是因为我想支持多选,并且我希望选择的类型是Set<TestObj>,而不是Set<UUID>。
有没有办法解决这个问题?
【问题讨论】:
-
为什么不将
id用作UUID()?另外我不太明白"I want to support multiple selection, and I want to get the actual reference to the object in the selection"是什么意思-List仍然在obj中为您提供参考。 ID 只是一个唯一的常量值,用于标识列表中的每一行。 -
@George 我的意思是选择将是 UUID 类型,所以每当我想获得关联的
TestObj时,我必须扫描数组(这对数千个项目) -
ID只是为了唯一标识它。显示选择位可能很有用 - 因为我看不出 ID 与它有什么关系。
-
@George 如果我更改列表中的
id:部分,那么选择的类型也必须更改。 (这取决于id:是什么) -
在
List中进行选择同时使用不同的id的一种方法是将tag(_:)修饰符(如.tag(obj))添加到列表行。然而,一些行仍然没有被释放(在测试大约 42 个 deinit,而之前为 0 个)。我认为解决方案可能类似于 Swift Collection 的OrderedDictionary。这样您就可以保存对象,但每个对象的关键是id。然后,您可以在 O(1) 时间内访问它们,并且不再通过引用类型来识别或标记项目。
标签: swift macos swiftui memory-leaks