【iPhone】メモリ不足時のシミュレートとデバッグ

Posted by: daichi  /  Category: iphone開発

iPachiで起きていた不具合なのですが、
特定の画面を表示中にメモリ不足に陥り
didReceiveMemoryWarningを受け取ると
アプリがクラッシュするという問題をついに
解消しました。

didReceiveMemoryWarning後にクラッシュするので
メモリ管理でどこかがおかしくなっているのだろうとは
予想がつくのですが、いかんせん貧弱なエラーメッセージの
ため、まったく発生元がつかめませんでした。
通常のデバッグコンソール
EXC_BAD_ACCESSとか言われてもさっぱりわからんです。

が、すばらしい記事をみつけました。
NSDebugEnabled

これでクラッシュをおこしているオブジェクトの生成場所を
特定できるので、格段にデバッグ効率があがります。

というわけで、エミュレータでのメモリ不足時のシミュレートと
デバッグのための設定をまとめます。

エミュレータでのメモリ警告のシミュレート

これは簡単です。
iPhone Simulatorをアクティブにして、
メニュー→ハードウェア→メモリ警告をシミュレート
とするだけです。

メモリ警告をシミュレート

これで、エミュレータはメモリ不足時の動作をとります。
つまりはAppDelegateでapplicationDidReceiveMemoryWarningが呼ばれ,
各コントローラでもdidReceiveMemoryWarningが呼ばれます。

デバッグの設定

デバッグコンソールに今よりはましなメッセージを出すことと、
クラッシュをおこしたオブジェクトを特定するコマンドを使える
ようにする設定をします。

まずはXcodeメニューで
メニュー→プロジェクト→アクティブな実行ファイルを編集

デバッグ設定1

開いた画面の引数タブ→環境変数に以下の3つを追加
NSZombieEnabled = YES
MallocStackLogging = YES
NSDebugEnabled = YES

デバッグ設定2

設定は以上。

デバッグしてみる

設定後に冒頭と同様のエラーをおこした時のコンソールは
こんな感じに表示されます。

デバッグ中1

EXEC_BAD_ACCESSだった部分に
-[CALayer retainCount]: message sent to deallocated instance 0x54d140
なんていうメッセージが表示されます。

なるほど。
既にdeallocされてしまったCALayerオブジェクトにretainCountメッセージを送ってしまってくラッシュしていたのか、という所までわかります。

ただ、CALayerはすべてのUIViewにあるし、どのUIViewのことだかはまだわからんと。

ここでデバッグコンソールに以下のコマンドを打ち込みます。
shell malloc_history {PID} {アドレス}

PIDにはアプリのプロセスIDを、アドレスにはクラッシュしたインスタンスのアドレスを入力します。

こんな感じ。コンソールの一番下のコマンドです。
デバッグ中2

このコマンドを打ち込んでENTERを押すと
ベロベロっとクラッシュしたインスタンスの生成場所が
表示されます。

デバッグ中3

この出力結果を下からたどっていくと、おそらく自分がコードを書いたメソッドにたどり着くはずです。
ここでは[RootViewController createToolBar]に見覚えがありました。
その中の+[UIButton buttonWithType:]メソッドを呼んでいる部分のよう。

該当部分のコードはこちら

?View Code OBJECTIVE-C
	// infoボタン
	UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
	[infoButton addTarget:self action:@selector(flipInfo:) forControlEvents:UIControlEventTouchUpInside];
	UIBarButtonItem *info = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
	[infoButton release];

コードはinfoマークのボタンを生成して、ツールバーにセットしている部分。
infoButtonを生成しているのは+[UIButton buttonWithType:]で
autoreleaseされるインスタンスを返す初期化メソッドなのに
[infoButton release]として、その場で解放してしまっていました。

そういうことかと。
ここでreleaseして既にdeallocされていたインスタンスに
retainCountメッセージが送られてしまっていたという。

ひとまず[infoButton release]を削除して
エラーが起きないことを確認。

このデバッグ方法は強力でした。
まだまだjavaやC#に比べると原因をたどりづらいですが、
前よりはずいぶんましになりました。

関連のあるアプリ

islot_icon
iSlot Pro 2009/03/13 リリース
ファイナンス 350円
パチスロ収支管理アプリ
app_store_badge

10 Responses to “【iPhone】メモリ不足時のシミュレートとデバッグ”

  1. bitlink Inc. Says:

    わかりやすい説明がされていたので、リンクを貼らせてもらいました。問題があれば削除しますので、メールに連絡してください。

  2. daichi Says:

    >bitlink Inc.さん
    リンク全く問題ありませんよ。
    また遊びにいらしてください。

  3. Seasons Says:

    当方、Xcode 3.2.2で試してみましたが、
    YESの代わりに1を設定することで動作しました。

  4. shin-go Says:

    デバッグが格段に楽になって、たすかりました。
    ありがとうございます。

  5. daichi Says:

    Seasonsさん情報ありがとうございます!また遊びにきてくださいー

  6. daichi Says:

    >shin-goさん
    お役に立てて幸いです。

  7. メモリ管理で四苦八苦。 « ( ・ρ・)っ[A passing fancy] Says:

    [...] ◆参考URL 【iphone】メモリ不足時のシミュレートとデバッグ iPhone開発 didReceiveMemoryWarningとviewDidUnloadの順番 10 iPhone Memory Management Tips [...]

  8. MKMapViewを利用する際の注意 | 404 odakoh Not Found Says:

    [...] [...]

  9. Keis Says:

    非常に悩んでいた問題を解決出来ました!

    本当に感謝します!

  10. miz Says:

    途方にくれていたところ、この方法でなんとか進めそうです!
    ありがとうございます。

コメント