在视图内部初始化@ObservedObject时发生内存泄漏

时间:2019-11-17 05:12:48

标签: ios swift swiftui

我有一个ViewModel类,ObservableObject,并且在初始化其对应视图时对其进行了初始化。看来,如果我对视图中的ViewModel有任何绑定,ViewModel就会泄漏。

例如,如果我在工作表中显示该视图,则每次我出示工作表时,都会分配一个新的引用,并且在解除工作表时不会取消分配该引用。我提交工作表时,引用计数一直在增长。

我是否缺少某些东西,还是不应该使用@ObservedObject属性包装器?

这是一个显示问题的简单示例。 deinit永远不会调用ViewModel函数

struct NewContactView: View {

    class ViewModel: ObservableObject {

        @Published var firstName = ""
        @Published var lastName = ""
        @Published var email = ""
        @Published var phoneNumber = ""

        init() {
            print("INIT")
        }

        deinit {
            print("DEINIT")
        }

    }

    @ObservedObject private var viewModel = ViewModel()

    var didCreateNewContact: (Contact) -> Void = { _ in }

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Names")) {
                    TextField("First Name", text: $viewModel.firstName)
                    TextField("Last Name", text: $viewModel.lastName)
                }

                Section(header: Text("Details")) {
                    TextField("Email", text: $viewModel.email)
                    TextField("Phone Number", text: $viewModel.phoneNumber)
                }

                Button(action: {}) {
                    Text("Save")
                }
            }
        }
    }
}

编辑:添加了显示工作表的代码

struct ContactsListView: View {

    @EnvironmentObject var contactStore: ContactStore

    @State private var isCreatingNewContact = false

    var body: some View {

            List(contactStore.contacts) { contact in
                ContactListItem(contact: contact)
            }
            .navigationBarItems(trailing: Button(action: { self.isCreatingNewContact = true }) {
                Image(systemName: "plus")
            })


            .sheet(isPresented: $isCreatingNewContact) {
                NewContactView(didCreateNewContact: self.createNewContactHandler)
        }
    }


    private func createNewContactHandler(_ contact: Contact) {
        contactStore.contacts.append(contact)
        isCreatingNewContact = false
    }

}

编辑2:内存图屏幕截图

Memory graph

编辑3:奇怪的是,用VStack替换Form视图摆脱了泄漏。如果我使用列表视图而不是VStack,则泄漏会再次出现。

3 个答案:

答案 0 :(得分:0)

它看起来没有泄漏。这是我的视图设置(在屏幕上添加了标题,以方便参考-代码在解释之后)。

导航

User Setup-> Contact Related-> Add Contact

,然后返回主屏幕User Setup,然后导航到下一个屏幕,将释放先前创建的视图模型实例。注意:我在模型中添加了唯一ID,该ID在初始化和取消初始化模型时会打印ID。

This leads me to believe that Models being observed are not deinited as soon as the view that is using it is getting released.  Swift runtime controls when the observable model should be released.  Also when I did the profiling for abandoned memory, I did not see any increase in the memory growth.  Also when the previously instance of the view model is getting released, there is a dip in the memory which proves the point that it is getting deallocated at that moment.   
struct ContentView: View {
    var body: some View {

        NavigationView {
        VStack {
             NavigationLink(destination: ContactRelated() ) {
                 Text("Contact Related")
             }
             Spacer()
        }
        .navigationBarTitle("User Setup")
        }
    }
}
struct ContactRelated: View {

    var body: some View {

        VStack {
             NavigationLink(destination: NewContactView() ) {
                 Text("Add New Contact")
             }
             Spacer()
        }
        .navigationBarTitle("Contact")
    }
}
struct NewContactView: View {

    class ViewModel: ObservableObject {

        var id = UUID()

        @Published var firstName = ""
        @Published var lastName = ""
        @Published var email = ""
        @Published var phoneNumber = ""

        init() {
            print(">>>init \(id)")
        }

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

    }

    @ObservedObject private var viewModel = ViewModel()

    //var didCreateNewContact: (Contact) -> Void = { _ in }

    var body: some View {

        Form {

            Section(header: Text("Names")) {
                TextField("First Name", text: $viewModel.firstName)
                TextField("Last Name", text: $viewModel.lastName)
            }

            Section(header: Text("Details")) {
                TextField("Email", text: $viewModel.email)
                TextField("Phone Number", text: $viewModel.phoneNumber)
            }

            Button(action: {}) {
                Text("Save")
            }
        }
    .navigationBarTitle("Add Contact")

    }
}

答案 1 :(得分:0)

似乎是一个错误。使用ScrollView代替List对我有用。

答案 2 :(得分:0)

使用@StateObject 这样它只会初始化一次