特定の条件で rootViewController を差し替えるとメモリリークする件

UIApplication.shared.keyWindow?.rootViewController で画面を差し替えたい時がありますよね?

例えばどんな時があるかとかと言いますと

  • 認証画面があり signup/signin 後に画面を切り替え
  • sigup/signin 後にチュートリアルの画面を表示
  • signout 後に認証画面を表示

などがあるかなと思います。

タイトルの通り、とある条件を満たして rootViewController を切り替えると差し替える前の viewController が解放されずに残り続けてしまいます。

これは iOS8 頃から認識していた問題ではありますが、iOS10 現在になっても修正されていません。

rdar://21404408: Memory leak in iOS 8+ after setting window.rootViewController while another view controller is presented

これのタチが悪いところは今使用されているメモリのほぼ全てがリークしてしまうといったことです。 今あなたのアプリがメモリを100MB使用しているとすると、それがそのままメモリリークしてしまうのでいきなり 100MB の負債を担うことになります。

これを解放するためにはアプリを App Switcher からアプリを kill するしかありません。

なんどもやってしまうと芋づる式にメモリリークしていっちゃいます。 かなり辛いですね。

どうすればメモリリークするのか。 それは 「モーダルが表示されている状態で rootViewController を差し替える」です。

この太字のとこおがかなり重要です。 つまりは モーダルが表示されている場合 モーダルを閉じてから rootViewController を差し替えればいいといった話ですね。

簡単に試せるようにサンプルプロジェクトを作りました。

github.com

dissmissの部分コメントアウトして試してもらえると、メモリリークすることが確認できます。

メモリリークするとき f:id:dealforest:20161203042740p:plain

メモリリークしないとき f:id:dealforest:20161203042721p:plain

signout したり画面を切り替える度にメモリリークしていて悩んでいる方は、一度確認してみるといいのではないでしょうか。

追記

そもそもwindow.rootViewControllerを差し替えるとうのがあまり良くないので、空のビューコントローラでいいのでそれをRootにして、それはずっと変わらないように保って、それ以下のコントローラを付け替えるようにするのがいいです。

コメントで教えてもらったのですが window を差し替えるよりも、この方がアニメーションもつけやすくていいですね。 勉強になりました。