2013年12月9日月曜日

Xcode 5 で、発生した NSException の詳細が表示されない時の対処法

原因がよくわからないのですが、iOSアプリをデバッグ中にNSExceptionが発生してアプリがクラッシュしてしまった時、その詳細がXcodeのコンソール上に表示されなくなってしまいました。普通はデフォルトのexception handlerがうまい具合にやってくれるのですが、何らかの理由でそれがうまくいかない場合があるようです。自分でスレッド立ててるとかでしょうか・・・


上図のように、例外が発生している箇所にブレークポイントをおいてどこで発生したのかを知ることはできるのですが、実際には発生箇所がわかっても発生原因がさっぱりわからないというケースもあります。例えばiOSのシステムが例外を発生させたときや、コードが公開されていないライブラリが例外を発生させたときなどです。

さて、このようなときは発生しているNSExceptionのdescriptionを直接読めれば便利そうです。というわけで調べてみました。

http://stackoverflow.com/questions/384775/how-do-i-find-out-what-exception-was-thrown-in-the-xcode-debugger-for-iphone
After you stop in the debugger, you can enter po $eax (simulator) or po $r0 (device) to see the exception. This is because the exception object is passed as the first argument to objc_exception_throw, which is kept in register r0 or EAX.
こちらの投稿のコメントにちょっとだけ書かれている方法がどうやら一番良さそうです。というわけで試してみました。
  1. まずはプロジェクトのAll Exception Breakpointを有効にします。
  2. アプリをデバッグして、例外が発生する箇所を実行します。
  3. 例外発生箇所でブレークポイントが発動したら、左のスタックトレースナビゲーションから、スタックトレースの一番上(objc_exception_throw)を選択します。
  4. コンソール上で以下のコマンドを実行します。
    • シミュレータなら、po $eax
    • 実機上なら、po $r0
うまくいきました!これで万一発生した例外の原因がよくわからない場合でも安心です。

余談ですが、eaxレジスタ/r0レジスタは関数の引数だかなんだかに相当するレジスタみたいなので、よくわからないコードで詰まっている場合はここを見れば他のケースでも解決できるかもしれません。

2013年11月27日水曜日

Mac の Skype のデータベースを最適化してパフォーマンスを向上させる小技



もりよしさんにSkypeで教えてもらったのですが、Mac版のSkypeクライアントってデータベースにsqlite3を使用しているんですね。少なくとも3年前には既に知られているネタみたいなのですが、ぜんぜん知りませんでした。
http://d.hatena.ne.jp/shishimaruby/20101214/1292288183

というわけで、Mac版のSkypeをお使いの方は
~/Library/Application Support/Skype/(自分のアカウント名)/
以下にsqlite3のデータベースファイルがあるので、Skypeを一度終了した状態で
sqlite3 main.db VACUUM
sqlite3 main.db REINDEX
という感じでVACUUMREINDEXを実行すると劇的にパフォーマンスが改善します。特に仕事とかで大量のログを見ている方におすすめです。私の場合、370MBもあったログが270MBまで減って、Skypeの起動も高速化しました。

※注意: このコマンドはSkypeのログデータベースを直接操作します。最悪全てのログが失われる危険があります。何を言っているのか分からない、ターミナルの操作なんて知らないという人は実行しないことを強くおすすめします。

Unity の PostprocessBuildPlayer を Ruby で書いてみる(第二版)

UnityでiOSのアプリを作っていて困ることの一つに、iOSが提供するシステムフレームワークへのリンクをプロジェクトに追加するのが超面倒くさいという問題が挙げられます。UnityがiOSアプリを書きだした後、手動でXcode上からシステムフレームワークを追加してもいいのですが、これはとんでもなく面倒です。というわけで、以前こちらの記事でRubyのxcodeprojモジュールを利用して自動的にシステムフレームワークを追加する方法をご紹介しました。
http://akisute.com/2012/09/unity-postprocessbuildplayer-weak.html

今回はそのPostprocessBuildPlayerをさらに機能拡充しましたのでご紹介いたします。主な機能として、
  • システムフレームワークへのリンクをプロジェクトに追加する
    • dylib, framework両方に対応
    • required, optional両方に対応
  • 空のinfo.plistをプロジェクトに追加する
    • ja, enに対応
    • Unity 3時代に空のinfo.plistを追加しないとiOSが提供するUIが英語で表示される問題が合ったため追加
    • Unity 4以上であれば修正されているかも
  • ヘッダサーチパスをライブラリサーチパスからコピーして自動設定する
    • Unity 3時代にビルドにこける事があったので追加
    • Unity 4以上であれば修正されているかも
  • ローンチイメージを自動設定する
    • 容量の関係で極限まで圧縮したjpgをpngの代わりに使いたいということで追加
    • 現在のXcode 5/iOS7向けの環境ではjpgを利用したDefault.pngは全く考慮されていない用に見えるので、使わないほうが無難だと思います
  • main.mmの書き換え
    • ここではSystem.Net.Socket.SocketがSIGPIPEを飛ばしてアプリ全体をクラッシュさせてしまうことがある問題を回避するためにsignalを捕まえたりしています
    • 変更規模が大きいならわざわざここでやるよりUnity側の/Assets/Plugins/iOSにmain.mmを置くほうが良いかと思いますが、ちょっと書き換えるだけなら有用です
  • AppController.mmの書き換え
    • 変更規模が大きいならわざわざここでやるよりUnity側の/Assets/Plugins/iOSにAppController.mmを置くほうが良いかと思いますが、ちょっと書き換えるだけなら有用です
インストール方法は、
  • まずソースコードを取ってきてPostprocessBuildPlayerという名前でUnityプロジェクトの/Assets/Editor/以下に配置します。
  • 実行にはRubyとバージョン0.4.xのxcodeprojモジュールが必要になりますので、インストールします。より大きいバージョンのxcodeprojでは動作未確認ですので、0.4.x系を指定することをおすすめします。
    • sudo gem install xcodeproj --version '~>0.4.0'

ソースコードはこちらになります。MITライセンスです。

余談になりますが、最近ではRubyのxcodeprojを使うのではなく、Pythonのmod-pbxprojを使う方法もあるみたいです。@Seasons氏はこちらの方法を使われているそうです。Pythonのがいい!という方はいかがでしょうか。