モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

NavigationLink + Object 生成パターンの注意事項

 もしかすると、画面が表示されなくてもオブジェクトが残っていて副作用が発生するかもしれません。

 素晴らしき解決案:

stackoverflow.com

1. 新しく ViewModel を生成し直すパターンなどは注意

 NavigationView のデフォルトのスタイル .automatic はおそらく簡単なアプリケーションだとうまくキャッシュが機能していい感じになると予想しています (内部で View などもキャッシュをしているのだと予想)。

 ただ、重量級アプリケーションで、リスト表示で in-memory するデータと、詳細画面で追加ロードするとかいった場合、これが副作用になる恐れがある(詳細画面から戻ってもすぐに解放されない)。

NavigationView {
  List {
    ForEach(content.content) { (item: ItemViewModel) in
      NavigationLink {
        LazyView(ContentView(ItemViewModel(id: item.id)))
      } label: {
        Text("\(item.id)")
      }
    }
  }
  .navigationTitle("Memory Leak Test")
}

 例えばこのような簡単なパターン。要素数は 6 としよう。そうすると、ItemViewModel は 6 初期化される。そして、詳細画面に行くときに ItemViewModel は初期化され、これで in-memory で合計 7 要素存在する。で、ここで頭のキレる人なら、詳細画面から戻ったら、要素数が 6 に戻ることを期待すると思う。

2. よくわからんが要素数は 7

 そこでおまじないをつけます。明示的に stack style と定義すればおk。

NavigationView {
  List {
    ForEach(content.content) { (item: ItemViewModel) in
      NavigationLink {
        LazyView(ContentView(ItemViewModel(id: item.id)))
      } label: {
        Text("\(item.id)")
      }
    }
  }
  .navigationTitle("Memory Leak Test")
}
.navigationViewStyle(.stack) // <- Magic Word!!!

 ね、簡単でしょ? これで期待する動作になります。

 ちなみに、watchOS 8.3 でも同じような動作になりました。

3. こねくり回して挙動を研究してみるのもいいです。

 簡単なサンプルを置いておきます。

github.com

 新年早々の記事でした。