从列表中删除的视图会泄露

时间:2019-07-10 14:32:16

标签: swiftui

以下示例显示了MyView内部的视图(List)。点击后,将其完全删除。但是,它的内存永远不会释放。

这很明显,但是MyView具有SomeClass类型的属性,并且从未调用其deinit的事实。但是,如果在List之外拍摄,则视图已正确放置。

这是另一个错误吗?

import SwiftUI

struct ContentView : View {
    @State private var showView = true

    var body: some View {
        VStack {
            List {
                if showView {
                    MyView().tapAction {
                        self.showView.toggle()
                    }
                }
            }
        }
    }
}

struct MyView: View {
    private var myVar = SomeClass()

    var body: some View {
        Text("Tap me to Remove!")
    }
}

class SomeClass {
    init() { }

    deinit {
        print("deinit SomeClass")
    }
}

但是,在这种情况下,deinit被正确调用。唯一的区别是在这里我不使用List

struct ContentView : View {
    @State private var showView = true

    var body: some View {
        VStack {
            if showView {
                MyView().tapAction {
                    self.showView.toggle()
                }
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

我认为这是一个错误。但是如果您将列表更改为

List(0...0) { _ in
                if self.showView {
                    MyView().tapAction {
                        self.showView.toggle()
                    }
                }
            }

无法正常工作,所有Scrollable视图(例如ScrollView或Form)也存在问题

答案 1 :(得分:1)

实际上这不是错误。 List的工作方式有所不同(有点像应用了一些android逻辑)。由于项目是可重用的,并且隐藏/显示操作是列表中非常常见的操作,因此它们决定将项目保留在内存中,以防万一您决定将其还原(就像android处理活动的方式一样)。

仅当其他尝试替换它们时,它们才被取消初始化。尝试使用此代码并研究UUID,以了解自己。我敢打赌,您会对结果感到震惊:

struct ContentView: View {
    @State private var items = [0, 1, 2, 3]

    var body: some View {
        VStack {
            List(items) { id in
                MyView().tapAction {
                    print("Tapped")
                    self.items.removeLast()
                }
            }
        }
    }
}

struct MyView: View {
    private var myVar = SomeClass()

    var body: some View {
        Text("Tap me to Remove!")
    }
}

class SomeClass {
    init() {
        id = UUID().uuidString
        print("Init id: \(id)")
    }

    let id: String

    deinit {
        print("deinit id: \(id)")
    }
}

如您所见,当您从列表中删除某项时,令人惊讶的是其余项被取消了初始化。因为这些被替换了,而不是您要删除的那个。

请注意,此行为尚未记录(可能),将来可能会改变。