Xcode
iOS
debug
LLDB

Symbolic Breakpoint を使って画面遷移毎に表示中のビューコントローラのクラス名を表示する

モチベーション

『画面遷移ごとに表示中のビューコントローラのクラス名を確認したい』

画面構成とビューコントローラクラス名の関係を把握していない場合など、
上記のような場面に遭遇することがあります。

各ビューコントローラクラスにログ出力処理を仕込んだり、
その後、不要になったログ出力処理を削除するのは面倒です。

このような問題を解決する方法として、
Symbolic Breakpoint を利用した方法を紹介したいと思います。

Symbolic Breakpoint

Symbolic Breakpoint とは特定のメソッドが呼び出されたときブレークさせるためのブレークポイントです。

ブレークポイントを追加する

Breakpoint navigator を表示して、 Symbolic Breakpoint... から追加します。

スクリーンショット 2018-07-28 18.36.47.png

次に、追加されたブレークポイントをダブルクリックして Symbol を指定します。
例えば、Symbol として -[UIViewController viewDidLoad] を指定した場合は、
全ての UIViewController の viewDidLoad でブレークさせることができます。

スクリーンショット 2018-07-28 18.39.37.png

$arg1, $arg2, $arg3

Symbolic Breakpoint でブレークすると次のオブジェクトを参照することができます。

変数 意味
$arg1 メッセージのレシーバ
$arg2 メッセージ
$arg3 メッセージの引数

例えば、Symbol として -[UIViewController viewDidLoad] を指定した場合は
$arg1 には UIViewController のオブジェクト、 $arg2 には viewDidLoad が設定されます。
viewDidLoad は引数を取らないので $arg3nil です。

Symbolic Breakpoint でブレークした際に、
lldb でオブジェクトの description を表示する場合は次のように操作します。

(lldb) po $arg1
<<クラス名>: アドレス>

(lldb) po (SEL)$arg2
"viewDidLoad"

(lldb) po $arg3
<nil>

スクリーンショット 2018-07-28 18.43.54.png

ブレークポイント通過時に LLDB コマンドを実行する

ブレークポイントで停止させずに自動で通過させて、通過したタイミングで LLDB コマンドを自動で実行させることもできます。

Automatically continue after evaluating actions にチェックを入れて、
add action で po $arg1 を設定します。

スクリーンショット 2018-07-29 13.55.33.png

これでブレークポイントで停止せずに、通過したタイミングで po $arg1 が実行されます。

output.gif

画面遷移のタイミングでクラス名を表示させる

それでは本題です。

Symbol に -[UIViewController viewDidAppear:] を設定します。
$arg1$arg2 は、フォーマットして出力した方が見やすいので、次のような NSString オブジェクトで出力してみます。

po [[NSString alloc] initWithFormat:@"🔰🔰🔰 %@ -[%@ %s]", [NSDate date], $arg1, $arg2]

これを Symbolic Breakpoint の Action の Debugger Command として設定します。

あとは、コンソールを 🔰🔰🔰 でフィルタするなどすれば、
viewDidAppear の都度、ビューコントローラクラス名などを確認することができるようになります。

viewDidAppear.gif

独自の LLDB コマンドとして定義する

ブレークポイント追加のたびに po [[NSString alloc] 〜 と記述するのは面倒なので、
これを独自コマンド pm (print method) として定義します。

独自コマンドの定義の仕方は次の通りです。

  • 独自コマンドを定義した python スクリプトを作成
  • python スクリプトを ~/.lldbinit-Xcode( または ~/.lldbinit )で読込む

python スクリプトを作成

独自コマンドを定義した python スクリプトを作成します。
今回は ~/.lldb/ 配下に print_method.py として作成しました。

~/.lldb/print_method.py
# coding: utf-8
#!/usr/bin/python

import lldb
import commands
import os

# name: pm(print method)
command_name = "pm"
# implementation: print -[<ClassName: address> message]
command_implementation = "po [[NSString alloc] initWithFormat:@\"🔰🔰🔰 %@ -[%@ %s]\", [NSDate date], $arg1, $arg2]"

def cmd(debugger, command, result, dict):
    debugger.HandleCommand(command_implementation)

def __lldb_init_module(debugger, internal_dict):
    function = cmd.__name__
    file_name = os.path.basename(__file__).split(".")[0]
    debugger.HandleCommand("command script add -f %s.%s %s" % (file_name, function, command_name))

~/.lldbinit-Xcode で読込む

先に作成した python スクリプトを ~/.lldbinit-Xcode に読み込ませます。

~/.lldbinit-Xcode
command script import ~/.lldb/print_method.py

その後 Xcode を再起動します。

再起動することで、 .lldbinit-Xcode が読み込まれて、
LLDB デバッガで独自コマンド pm が使用可能になります。

そして、独自コマンドは Symbolic Breakpoint の Action として使用することもできます。
これで、ブレークポイントの設定の手間を省くことができました。

Symbolic Breakpoint に独自コマンドを設定する

独自コマンド pm が使えるようになったので、Debbuger Command に pm と設定するだけで
ビューコントローラクラス名をコンソール出力できるようになりました。

スクリーンショット 2018-07-29 14.34.06.png

pmviewDidAppear に限らず、任意の Objective-C オブジェクトの任意のメソッドで利用できるので、
工夫次第で、Symbolic Breakpoint を利用する際の便利なツールになると思います。

ということで、Symbolic Breakpoint を使ってデバッグライフを楽しみましょう。

参考