このエントリーをはてなブックマークに追加このエントリをつぶやく

デバッグコンソールを活用しよう

XcodeのコンソールをNSLogの出力結果を確認するためだけの場所だと思っていませんか?

ええ、筆者は思っていました。
Javaでの開発環境にEclipseを使っていると、コンソールは実質的に出力専用でした。標準入力を受け取ることもできますが、自分でそういうコードを書かない限りは使いませんし。

Xcodeはデバッガとしてgdbが使われており、コンソールはそのままgdbのコマンドラインインタフェースになっています。
Xcodeでアプリ実行中に一時停止ボタンを押すと、コンソールにgdbのプロンプトが出ます。ブレークポイントやクラッシュによって停止したときも同様です。
ここで対話的にコマンドを実行することができます。
gdbには呆れるほどたくさんのコマンドが用意されていますが、基本的な操作はXcodeのGUIからも実行できるので無理に覚える必要はないと思います。
しかし、print-objectコマンドだけは覚えておいて損はないでしょう。

次のように使います。
[ccN_text]
(gdb) print-object [NSDate date]
2011-04-26 06:49:29 +0000
[/ccN_text]
引数を評価して得られるオブジェクトのdescriptionメソッドの結果が出力されます。
とくにこだわりがなければ省略形のpoコマンドを使うことになります。
[ccN_text]
(gdb) po [NSDate date]
2011-04-26 06:59:00 +0000
[/ccN_text]

止めた場所がメソッド内ならローカル変数やインスタンス変数も見られます。
[ccNe_text]
(gdb) po tableView
<UITableView: 0x5032e00; frame = (0 0; 320 416); clipsToBounds = YES; autoresize = W+H; layer = ; contentOffset: {0, 0}>
[/ccNe_text]

さて、ここからが本題。
単純に変数の内容を見るだけならXcodeの変数ペインを見れば十分です。
そこで以前に紹介したrecursiveDescriptionが出てきます。

適当なビューコントローラのメソッドにブレークポイントを仕掛けてアプリを一時停止させ、以下のコマンドを実行します。
[ccN_text]
(gdb) po [[self view] recursiveDescription]
(…実行結果は割愛…)
[/ccN_text]
この方法ならソースにデバッグコードを記述しなくても、必要なときにビュー階層を見られます。
なお、ドット記法が使えないので、[cci_objc][self.view recursiveDescription][/cci_objc]とは書けません。

難点はrecursiveDescriptionを毎回入力するのが大変なことです。
カーソルキーの上下で入力履歴を呼び出したり、タブキーの入力補完を活用したとしても、まだ億劫だと思う人がいるかもしれません。

では、ユーザ定義コマンドを作りましょう。

[ccNe_text]
(gdb) define pv
>po [$arg0 $arg1 recursiveDescription]
>end
[/ccNe_text]

これでpvコマンドが定義されました。pvはprint-viewの略のつもりです。
引数にUIViewオブジェクトを与えるとビュー階層が出力されます。
[ccN_text]
(gdb) pv [self view]
(…実行結果は割愛…)
[/ccN_text]
実はユーザ定義コマンドの引数はスペースで区切られた文字列と認識されるため、上記の例だと”[self“と”view]“の二つの引数が渡されています。
定義に記述した$arg1というのがミソなのですが、このままだと単純にローカル変数等を渡したい場合に引数が足りずエラーが出ます。
[ccN_text]
(gdb) pv sender
Missing argument 1 in user function.
[/ccN_text]

以下は引数の数をチェックしてユーザ定義コマンドの上限である最大10個までの分割に対応したものです。
ついでに引数がないときはUIWindow以下のビュー階層を出力するようにしてみました。
[ccNe_text]
define pv
if $argc == 0
po [[UIWindow keyWindow] recursiveDescription]
end
if $argc == 1
po [$arg0 recursiveDescription]
end
if $argc == 2
po [$arg0 $arg1 recursiveDescription]
end
if $argc == 3
po [$arg0 $arg1 $arg2 recursiveDescription]
end
if $argc == 4
po [$arg0 $arg1 $arg2 $arg3 recursiveDescription]
end
if $argc == 5
po [$arg0 $arg1 $arg2 $arg3 $arg4 recursiveDescription]
end
if $argc == 6
po [$arg0 $arg1 $arg2 $arg3 $arg4 $arg5 recursiveDescription]
end
if $argc == 7
po [$arg0 $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 recursiveDescription]
end
if $argc == 8
po [$arg0 $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 recursiveDescription]
end
if $argc == 9
po [$arg0 $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 $arg8 recursiveDescription]
end
if $argc == 10
po [$arg0 $arg1 $arg2 $arg3 $arg4 $arg5 $arg6 $arg7 $arg8 $arg9 recursiveDescription]
end
end

document pv
print-view
pv … print window
pv <view> … print view
end
[/ccNe_text]

これをホームディレクトリ直下に.gdbinitというファイル名で保存すれば、毎回起動時に読み込まれていつでもpvコマンドが使えるようになります。

カテゴリー: プログラミング   タグ: ,   この投稿のパーマリンク

コメントは受け付けていません。