SwiftUI — Как отключить объект состояния при переходе назад?

#swift #navigation #swiftui

#swift #навигация #swiftui

Вопрос:

Я хочу, чтобы мой @StateObject деинициализировался как можно скорее после перехода назад, но кажется, что объект хранится в памяти. «Deint ViewModel» не печатается при обратной навигации, он сначала печатается после того, как я снова перейду к представлению, из которого я исходил. Есть ли способ освободить @StateObject из памяти при обратной навигации?

 struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: TestView(), label: { Text("Show Test View") })
        }
    }
}

struct TestView: View {
    
    @StateObject private var viewModel = ViewModel()
    
    var body: some View {
        Text("Test View")
    }
}

final class ViewModel: ObservableObject {
    deinit {
        print("Deint ViewModel")
    }
}
  

Ответ №1:

У меня нет блестящего ответа на все ситуации, которые препятствуют деинициализации @StateObject, но я обнаружил, что выполнение фоновых асинхронных задач предотвращает деинициализацию.

В моем случае у меня было зарегистрировано несколько отменяемых объектов для прослушивания PassthroughSubject и / или CurrentValueSubject (которые я использовал для обработки внешних изменений в моей модели и отображения результата в представлении), но я никогда не отменял их. Как только я сделал это в представлении, используя .onDisappear, это сработало.

Итак, все мои представления «подписываются» на модель представления (у меня есть метод ViewModel.subscribe()) с использованием .onAppear, а затем «отписываются» от модели представления (у меня есть метод ViewModel.subscribe()) с использованием .onDisappear. При этом @StateObject деинициализируется при отклонении представления.

Ответ №2:

Добавление к ответу GregP: если вы удалили все свои cancellables on onDisappear и deinit все еще не вызываетесь, вы можете использовать график памяти отладки График отладки памяти

Перейдите к объекту, посмотрите его дерево и посмотрите, что еще ссылается на него. Например, у меня это выглядело так: Дополнительная ссылка

Поскольку на этот объект ссылался другой объект, он не удалялся из памяти (ARC). Итак, все, что мне нужно было сделать, это удалить его из статуса делегата вместе с отменой cancellables вызова and deinit

Ответ №3:

Я думаю, вам следует использовать @ObservedObject private var viewModel: ViewModel вместо этого, а затем ввести новый экземпляр ViewModel извне testView

Комментарии:

1. Можете ли вы объяснить мне, как это поможет отключить ViewModel при переходе назад? Реальная проблема заключается в том, что при переходе назад testView все еще находится в памяти, поэтому ViewModel не деинициализируется, когда ожидалось. Я просто думаю, что это «особенность» NavigationView.

2. Если вы используете StateObject , ваша модель представления не будет уничтожена. вместо этого используйте observedObject . вы можете проверить donnywals.com /… чтобы более четко

3. Вызывается деинит объекта состояния. На данный момент я сталкиваюсь с проблемой, когда он не вызывается, у меня все еще есть некоторые вызовы async / await, запущенные на нем, но, если я этого не сделаю, де-инициализация вызывается в ViewModel.