はじめに
突然ですがデバッグ時にこんなコード書いてないですか?
if (self.isLogin) {
static int callCount;
if (++callCount >= 5) {
NSLog(@"self.view.recursiveDescription: %@", self.view.recursiveDescription);
}
}
特定のプロパティが有効な場合かつn回目に呼ばれた時だけログを出力するという意図です。これをブレークポイントでやろうと思っても、
- 変数のチェックとか特定の条件下のみブレークが出来ないし。。
- ブレークポイント設定すると毎回停止してしまうし。。
- そもそもログ出せないし。。
- デバッグコードをgitで共有したい場合もあるし。。
というデメリットがあるので、ついついNSLog()書いてデバッグしがちですよね。
否!!
1〜4全部できますよ!しかもブレークポイントだとコードを汚すことも無いです。コードを汚さないということはビルドし直す必要が無いし、間違ってログ出力のコードをリリースする心配が無い、さらにはロジックや挙動に影響しない、というメリットもあります。
本来ブレークポイントはプログラムを停止し、そこからステップ実行をするために使用されることが多いですが、Xcodeではブレークをトリガーに様々なアクションが設定できます。
ブレークポイントでできること
ブレークポイントの設定にいては先日のふざけた投稿を参考にしてください。
- 「Xcodeのブレークポイントで響子さんにシャウトしてもらおう」
http://qiita.com/caesar_cat/items/12ba13aa282c9b4c00c5
特定の条件下のみブレークする
ブレークポイントで停止する条件を指定できます。条件文は直接コードを書くことができます。条件はソースコード内ではなくブレークポイント設定ダイアログに直接条件文を入力するのでコードを汚す必要がありません。
条件文はブレークポイント設定ダイアログの Condition 欄に入力しますが、この時注意点が。デバッガはドットによる記法を理解しないので、メソッドとして呼び出すこと。また、その型も理解できるよう宣言しておくことです。例えば UIViewController の childViewControllers の配列要素が空である状態を条件とする場合は、
(int)[[self childViewControllers] count] == 0
と記述します。↓こんな感じ。
指定回数分のブレークを無視する
n+1回目にブレークポイントを通過した時だけブレークする、といった時には Ignore の設定項目が有効です。起動直後は無視して2回目以降にだけブレークしたい場合など意外と使用頻度は高いと思います。
下記の例だと2回の通過を無視して、3回めでブレークすることになります。
ブレークしないブレークポイント
ブレークポイントというくらいなので必ずプログラムが停止するするというイメージが強いですが、実は停止しなくても良いんです。正確にはブレーク時に停止するが自動で再開する、という挙動です。細かなアニメーションのタイミングなどや複数同時の通信シーケンス中に停止すると動作に影響する場合があるので、停止させない方が良い場合もあります。
停止させない方法は、ブレークポイント設定ダイアログの「Automatically continue after evaluating actions」にチェックを入れるだけです。
ブレークしなくて何するの?と思いますが、次項のログ書出しとの合わせ技でかなり重宝します。
ログを書き出す
Log Message Action を使用するとブレークポイント通過時にコンソールにログを出力できます。ログメッセージには埋め込みパラメータとして、%B でブレークポイントが置かれているメソッド名(or 関数名)、%H でブレークポイント通過回数が置換されます。また、@〜@の中にコードを入れることで実行時に評価した値を埋め込む事が可能です。(Condition時と同じくオブジェクトプロパティへのアクセスにドット記法が使えないこと、型宣言が必須ということに注意。)
これでもう if (〜) { NSLog() } みたいなデバッグコードは不要になりますね。
コードを実行する
もちろんデバッガコマンドも使用できます。Debugger Command Action で poコマンドを使用すればコードを評価できます。
例えばビューの AutoLayout の制約をコンソールに出力するには、
po [[self view] _autolayoutTrace]
とします。また、出力だけでなく変数代入やメソッド実行も可能です。
po self.view = nil;
とか
po [self.view removeFromSuperview]
とか出来ちゃいます。
ただ、書き込み系は挙動そのものを変えますが、リリースビルドしたバイナリには含まれないので、うっかりそのままリリースしないよう注意しましょう。
デバッガコマンドと言えば、bt をブレーク時に使用すると地味に便利です。bt はその時点のバックトレースのコールツリーをコンソールに出力します。メソッドがどこから呼ばれたかチェックするのに便利ですね。「bt -c 数値」とすることで数値分のバックトレースをコンソールに出力できます。
ちなみにこれらデバッガコマンドはプログラムをポーズしているときにコンソールに手入力することで実行可能です。
ブレークポイントの共有
さて、ブレークポイントの設定だけで実行時にコードを評価したり、ログを書いたり、これでもうNSLog()依存のデバッグは必要なくなりました。あとはこれらの設定が開発チーム内で共有できるとデバッグコードは不要になります。方法は簡単。ブレークポイントナビゲーター(⌘+7)を開いて共有したいブレークポイントを右クリック。メニューから「Share Breakpoint」を選択。以上です。
共有ブレークポイントの実体は、プロジェクトファイル内の、
プロジェクト名.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist
というファイルに記載されるので、これを git に追加してやればチーム内で共有可能です。
デメリット
ブレークポイント設定のデメリットが一つだけあります。それはUndo/Redoができないこと。うっかりブレークポイントを消してしまった時に復活ができない。共有設定をしていればgitから復元はできますが、共有していないポイントの変更は注意ですね。
まとめ
Xcode に限らず同等の機能を持った IDE は数多く存在します。が、意外とこれらを使いこなしている人は少ないんじゃないでしょうか?ブレークポイントの Action についてはシェル連携やGPU frameのキャプチャなど他にもいろいろあり、これらの複合アクション設定も可能なのでいろいろ活用できそうです。今後も Xcode のバージョンアップで Action が増えていくかもですね。
これを読んでデバッグが少しでも楽しく感じてもらえると嬉しいです。