事前に「問題:次のRubyMotionアプリにはメモリ関連の不具合があります。5分以内に原因箇所を特定せよ。」という記事を書いておきました。今回はこの問題の解決方法を書きたいと思います。まだ読まれていない方は、まずそちらから読んでみてください。
1. Instrumentsを使う
RubyMotion 2.12でInstrumentsを簡単に使えるようになりました。まずは、Instrumentsを使用して不具合があるかどうか確認しておきましょう。今回は"Zombies"というテンプレートを用います。
1
| |
rake profile:templatesを実行するとほかにどのようなテンプレートがあるか確認できます。
Instrumentsが起動したあと、アプリがクラッシュする手順を実行します。

メモリが解放されてしまったオブジェクトにメッセージを送信してクラッシュしていることがわかります。残念ながら、シンボルが削除されていてなにが原因なのかまではわかりません。
“Zombie Messaged"と表示されない場合には、今回の手順では不具合箇所を探すことができないので、別の方法を模索してください。
2. 意図せず解放されているオブジェクトを探す
以下のコードをapp/debug.rbなどの名前で保存してアプリに追加しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
RubyMotionやObjective-Cでは、オブジェクトが解放されるときにdeallocというメソッドが呼ばれます。また解放される前にautoreleaseが呼び出され解放するように指示されます。
意図せずdeallocが呼ばれているオブジェクトを探し、autoreleaseが呼ばれる場所がどこなのかを特定します。アプリを実行するとTerminalにログがいろいろ表示されます。

MyControllerがdeallocされていることがわかります。運良く、すぐ上にautoreleaseのログがありますね。app_delegate.rbの47行目でautoreleaseが呼ばれていることが分かります。該当する箇所は、以下のコードとなっています。
1
| |
コントローラのオブジェクトが気づかないうちに解放されたためにクラッシュしていました。インスタンス変数に格納して、長期的に保有するようにするとアプリが期待通りに動作するようになります。
1
| |
運が良いと、このような感じで簡単に原因箇所を割り出すことができます。万能ではないので、運が悪いと原因が見つからないかもしれません。
deallocとautoreleaseを上書きすることでメモリリークが発生したりすることもありますので、使い終わったらすぐに削除してください。
3. 余談
RubyObjectではなくNSObjectに対してメソッドをオーバーライドしてみたのですが、うまくいかなかったのでこのようになっています。
StringクラスがString->NSMutableString->NSString->NSObjectと継承しているせいなのかdeallocで文字列を扱うと、文字列生成->dealloc->文字列生成->dealloc->…と無限に繰り返されてしまい、クラッシュしてしまいます。
組み込みのStringクラスに影響を与えないためにRubyObjectを使用しています。おおよそ以下の図のような構成になっているはずです。このため、組み込みクラスやiOS SDKのクラスを継承しているものや、それらが影響して解放されてしまっているようなものは、今回の方法では検出できません。
