2010年2月24日水曜日

iPhoneアプリでプロセスは落ちずにメインのUIViewだけ落ちた場合の対処

iPhoneアプリの開発で色々と試していてわかったことだが、どうもプロセスそのものは落ちないまでも、メインのUIViewだけ落ちてviewDidLoad()が勝手に再び呼び出される場合があることがわかった。

動作を見ていると、バックグラウンドでiPodを再生していたり、トランジションを使ってモーダルビューコントローラーを表示した後や、プログラム内でメール送信するためにMFMailComposeViewControllerを使った後などに起こるようだ。

この状況になる前には大体didReceiveMemoryWarning()が呼び出されていることから、メモリ不足によるものではないかと思われる。

この症状の問題は、プロセスそのものは落ちていないため、フラグが直前の動作のものになっていたり、作成したオブジェクトを再作成することによるメモリリークなどを引き起こすことだ。タイマーを作っていたりすると、タイマーが2重登録されておかしなことになる。

今のところ考えられる対処は、こういう場合があることを前提に初期化メソッドでフラグのリセットやオブジェクトの開放などの処理を行うことだ。

通常こういった処理はdealloc()で行うことになっているが、dealloc()はプログラムで直接呼ぶなということにもなっているらしい。dealloc()にはsuperのdealloc()を呼び出す処理が入っているからだろう。

そこで、deallocLocal()などというメソッドを作り、その中で必要な処理を行うことにした。そして、このメソッドを初期化メソッドとdealloc()の両方で呼び出されるようにしておく。

viewDidUnload()で必要な処理を行うようにしてViewが落ちた時に自動的に必要な処理が行われるようにしてもいいかもしれないが、落ち方によっては期待できない場合もあるだろう。その場合も考えて、初期化メソッドで必要な処理が呼ばれるようにしておいた方がいいだろう。

JavaやActionScriptなどではこういった経験はなかったが、Objective-C、というよりiPhoneアプリの開発ではメモリがごく限られているのでこうした対処が必要になるのだろう。

追記1:
その後、この現象がメモリ不足で起こることがあるとドキュメントにハッキリ書いてあることがわかった。
iPhoneアプリケーションチュートリアル」 PDF p30 「nibファイルのロード」
メモリ不足の警告を受け取った結果、View Controllerがビューを破棄している場合は、必要であれば、loadViewが再度呼び出されて、そのビューが再作成されます

追記2:
iPhone開発ガイド」 PDF p38
・・・View Controllerでは、現在オフスクリーンのビューを全て消去します・・・アプリケーションにメモリリークがあったり、アプリケーションが依然として大量のメモリを消費し続けているなどの理由で、十分なメモリが開放されなかった場合、システムによってアプリケーションが強制終了される場合があります。・・・

今回の場合はオフスクリーンでなくオンスクリーンも消去されたのだが(^^);
多分オフスクリーンがなかったせいで、オンスクリーンを消去せざるを得なかったのだろう。