Watchpoint — ウォッチポイントの種類とXcodeでの設定方法

著者: IT Sectr 公開日: 2026-05-07 読了時間: 8 分

Watchpoint(ウォッチポイント)— 指定された変数やメモリ領域の値が変更されたときにプログラムの実行を一時停止するデバッグメカニズムです。ブレークポイントがコード行に紐づいているのに対し、ウォッチポイントはプログラムのどの部分で変更が発生してもデータの変化を追跡します。Apple Developer Documentationによると、ウォッチポイントはデータ競合のデバッグ、予期しないプロパティ変更、オブジェクトのライフサイクル追跡に不可欠です。

重要なポイント

  • Watchpoint — 変数の変更を監視するポイント。指定されたメモリ領域への書き込み時に作動します。
  • Xcode はGUIおよびLLDBコンソールを介してウォッチポイントをサポートしており、条件付きウォッチポイントも含みます。
  • オブジェクトのプロパティ変数には、変数名ではなくメモリアドレスを指定したウォッチポイントが必要です。
  • ウォッチポイントを使用すると、各書き込み操作のハードウェア監視によりパフォーマンスが大幅に低下します。
  • Watchpoints はデバッグセッション間で保持されません — 起動のたびに再設定が必要です。

ウォッチポイントとは何かと動作原理

Watchpoint — 指定されたメモリアドレスの値が変更されたときにプログラムの実行を一時停止するデバッガメカニズムです。ブレークポイントが特定のコード行に到達したときに反応するのに対し、ウォッチポイントは特定のメモリセルへの書き込みに反応します — コードのどの部分が書き込みを行ったかに関係ありません。これにより、特にマルチスレッドアプリケーションにおいて、予期しない変数の変更を見つけるのに不可欠です。

動作原理はプロセッサのハードウェアサポートに基づいています。最新のARMチップ(Apple Silicon、Qualcomm Snapdragon)は4~8個のハードウェアウォッチポイントレジスタを提供します。プロセッサがウォッチポイントに一致するアドレスへの書き込み命令を実行すると、ハードウェア割り込みが発生し、デバッガがそれをキャッチします。ハードウェアレジスタが枯渇すると、LLDBはソフトウェアモードに切り替わります — 各書き込み命令をステップ単位でチェックするため、実行速度が数十倍低下します。

ARM Architecture Reference Manualによると、ハードウェアウォッチポイントはData Watchpoint and Trace(DWT)モジュールレベルで動作し、実行可能コードの変更を必要としません。応答時間はナノ秒単位で、オーディオやビデオフレームバッファなどのデータ処理ループにおける高頻度の変数変更も追跡できます。

ウォッチポイントがブレークポイントより効果的な場合

Watchpoint は、どの変数が変更されているかは分かっているが、どこから変更されているかが分からない場合に不可欠です。典型的なシナリオ:UIViewのframeプロパティが明らかな理由なく変更される、バックグラウンドスレッドのカウンタが予測不能にリセットされる、isLoadingフラグがネットワークリクエスト完了前に切り替わる。これらのそれぞれの場合において、書き込みが発生する可能性のあるすべての場所にブレークポイントを設定するのは非現実的です。ウォッチポイントは単一のコマンドで問題を解決します。

ウォッチポイントの種類と設定方法

XcodeとLLDBは3種類のウォッチポイントをサポートしています:watchpoint set variable — ローカル変数の監視用;watchpoint set expression — アドレスを返す式の監視用;watchpoint set — 生のメモリアドレスの監視用。それぞれに適用領域があります。

Watchpointの種類LLDBコマンド用途
変数watchpoint set variable -w write self.countローカル変数とグローバル変数、構造体のプロパティ
watchpoint set expression -w write -- &self->mutex.lockポインタによる構造体フィールド、インデックスによる配列要素
アドレスwatchpoint set -w write 0x600000c4b80以前のLLDB出力からの特定のメモリアドレス

Xcode GUIを使用した設定

Xcodeでは、デバッグエリアを介してウォッチポイントを設定できます:ブレークポイントで停止し、Variables Viewパネルで目的の変数を見つけ、右クリックしてWatch Variableを選択します。Xcodeは自動的に正しい変数名とコンテキストでwatchpoint set variableコマンドを実行します。その後、デバッガは値が変更されるたびに停止します — コンソールに切り替えずに迅速なデバッグが可能です。ただし、この方法は変数が現在のフレームのスコープ内にある場合にのみ機能します。

swift
func processItems(_ items: [String]) {
    var index = 0
    // GUIでindexにウォッチポイントを設定:
    // ここで停止、右クリック → Watch Variable
    for item in items {
        index += 1
        print("アイテム \(index): \(item)")
    }
}

LLDBを使ったウォッチポイントの管理

LLDB はコンソールからウォッチポイントを管理するための完全なコマンドセットを提供します。GUIよりも制御性が高く、監視領域のサイズ設定、トリガー条件の指定、計算された式から取得したアドレスへのウォッチポイント作成、トリガー時の自動アクション実行が可能です。コマンドインターフェースは、監視パラメータを素早く変更する必要がある複雑なシナリオのデバッグに特に役立ちます。

基本的なウォッチポイントコマンド

watchpoint set variableコマンドはスコープを考慮して変数名を受け取ります:Objective-Cのselfプロパティにはself->_property、Swiftにはself.propertyを使用します。-w writeパラメータは書き込み追跡を設定し、-w read — 読み取り追跡(一部のアーキテクチャでのみ利用可能)、-s size — 領域サイズ(バイト単位)を設定します。ウォッチポイント設定後、watchpoint listコマンドでリストを表示できます。

lldb
(lldb) watchpoint set variable -w write -s 8 self.balance
Watchpoint 1: addr = 0x600000c4b80 size = 8 state = enabled  type = w
    watchpoint spec: 'self.balance'
(lldb) watchpoint set expression -w write -- self->items._storage
Watchpoint 2: addr = 0x600003a4c00 size = 8
(lldb) watchpoint list
1: location = 0x600000c4b80, type = write, variable = '_balance'
2: location = 0x600003a4c00, type = write, expression = 'self->items._storage'

条件付きウォッチポイント

ブレークポイントと同様に、ウォッチポイントは条件付きトリガーをサポートしています。-cパラメータはSwiftまたはObjective-Cで条件を設定します。たとえば、newValue > 1000という条件のウォッチポイントは、書き込まれた値が1000を超えた場合にのみ停止します。これは、毎秒数千の変更を生成するループやセンサーのデバッグ時に重要です — そうでなければデバッガはすべての変更で停止し、作業が不可能になります。

lldb
(lldb) watchpoint set variable -w write self.temperature -c "(int)$newValue > 100"
Watchpoint 3: addr = 0x600000e4a20, condition = '(int)$newValue > 100'
(lldb) watchpoint modify 3 -C "po self.temperature" -G true
// 値を自動的にログに記録して実行を継続

オブジェクトのプロパティとコレクションのウォッチポイント

Objective-C および Swift オブジェクトのプロパティに対するウォッチポイントの特有の特徴 — ウォッチポイントはプロパティ名ではなく、オブジェクトのメモリ内のivar(インスタンス変数)フィールドのアドレスに設定されます。つまり、新しいオブジェクトが割り当てられるたびに(たとえば、ViewControllerを再作成する場合)、メモリアドレスが変更されるためウォッチポイントは無効になります。再起動後もプロパティを継続的に監視するには、オブジェクトの初期化時にウォッチポイントを再設定する必要があります。

コレクション要素のウォッチポイント

配列辞書 の要素を監視するには、特定の要素のアドレスを計算する必要があります。たとえば、配列の3番目の要素を監視するには watchpoint set expression -- &array[2] を使用します。配列が内部バッファを再割り当てすると(容量を超えて要素を追加した場合)、ウォッチポイントは無効になります — LLDBは Watchpoint 1 has an invalid address エラーを報告します。このような場合、コレクションのサイズ変更後にウォッチポイントを再設定する必要があります。

lldb
(lldb) expr var $arr = [10, 20, 30, 40, 50]
(lldb) watchpoint set expression -w write -- &$arr[2]
Watchpoint 4: addr = 0x1000a4b20, size = 8
(lldb) expr $arr[2] = 99
Watchpoint 4 hit: old value: 30, new value: 99

ウォッチポイントによるretain countの追跡

Objective-Cでは、objc_object構造体のretainCountフィールドにウォッチポイントを設定することで、オブジェクトのretain countを追跡できます。LLDBは watchpoint set expression -w write -- (int*)[object retainCount] を介してこれを行います。ただし、ARC(自動参照カウント)を使用するSwiftオブジェクトの場合、retain countに直接アクセスすることはできません — 代わりにInstrumentsまたはMemory Graph Debuggerを使用してリーク解析を行ってください。Appleによると、retain countのウォッチポイントはARC最適化が無効になったDebugビルドでのみ機能します。

ウォッチポイントの制限とパフォーマンス

Watchpoints には考慮すべきいくつかの重要な制限があります。主な制限 — ハードウェアウォッチポイントの数はARMアーキテクチャで4~8個、x86で最大4個のレジスタに制限されています。すべてのハードウェアレジスタが占有されると、LLDBはソフトウェアウォッチポイントモードに切り替わります:追跡範囲内のすべての書き込み命令を変更して例外を生成します。これにより実行速度が10~50倍低下するため、実際には同時に2~3個以上のアクティブなウォッチポイントを使用しないことを推奨します。

2つ目の制限 — メモリ再割り当て時のウォッチポイントの無効化。ガベージコレクションやARCが作動し、オブジェクトがメモリ内で移動すると(ヒープ圧縮を行う言語の場合)、ウォッチポイントアドレスが不正になります。SwiftとObjective-CではARCはオブジェクトを移動しませんが、配列や文字列の再割り当ては同じ効果を引き起こします。LLDBは Watchpoint N address (0x...) doesn’t contain a valid allocation というメッセージでこれについて警告します。

3つ目の制限 — 変数の可視性。ローカル変数のウォッチポイントは、その変数が現在のスタックフレームのスコープ内にある間のみ機能します。関数が終了すると、ウォッチポイントは自動的に削除されます。グローバル変数や長寿命オブジェクトのフィールドを監視する場合、ウォッチポイントは watchpoint delete で明示的に削除されるか、プロセスが終了するまで持続します。

lldb
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete               # すべてのwatchpointsを削除
2 watchpoints deleted.
(lldb) watchpoint disable 1             # 一時的に無効化
(lldb) watchpoint enable 1              # 再度有効化

ARMによると、Apple Silicon(M1~M4)のウォッチポイントは1~8バイトの監視領域サイズをサポートしています。8バイトを超える構造体を監視するには、各フィールドに複数のウォッチポイントを設定する必要があります。これは CGRect(16バイト)や UIEdgeInsets(16バイト)などの複雑なデータ構造をデバッグする際に重要です。

よくある質問

ウォッチポイントとブレークポイントの違いは何ですか?

Breakpoint はコードの行に紐づいており、その行に到達すると停止します。Watchpoint はメモリアドレスに紐づいており、プログラムのどこからでもそのアドレスへの書き込みが発生すると停止します。ウォッチポイントは「誰が値を変更しているか」を探し、ブレークポイントは「この行で何が起こっているか」を探します。

同時にいくつのウォッチポイントを設定できますか?

ARM(Apple Siliconを含む)では4~8個のハードウェアウォッチポイント。これを超えるとウォッチポイントはソフトウェアモードに切り替わり、実行速度が10~50倍低下します。同時に2~3個以上のアクティブなウォッチポイントを使用しないことを推奨します。

アプリケーション再起動後にウォッチポイントが機能しなくなるのはなぜですか?

Watchpoints はデバッグセッション間で保持されません。新しい起動のたびにメモリアドレスが変わるため、ウォッチポイントを再度設定する必要があります。例外は固定アドレスを持つグローバル変数のウォッチポイントです。

Swiftのプロパティにウォッチポイントを設定できますか?

はい、ただしウォッチポイントはプロパティ自体ではなく、プロパティのivar(バッキングストレージ)に設定されます。Swiftでは watchpoint set variable self.property を使用します — LLDBがプロパティ名から対応するivarを自動的に見つけます。

ウォッチポイントはアプリケーションのパフォーマンスにどのような影響を与えますか?

ハードウェア ウォッチポイントはパフォーマンスに影響しません — 割り込みはプロセッサレベルで発生します。ソフトウェア ウォッチポイント(ハードウェアレジスタが不足した場合)は、LLDBがすべての書き込み命令をチェックするため、実行速度が10~50倍低下します。

まとめ

  • Watchpoint — メモリ変更を監視するポイント。任意のスレッドから指定されたアドレスへの書き込みで作動します。
  • ハードウェア ウォッチポイントはプロセッサのDWTレジスタを使用し、実行速度を低下させませんが、4~8個のレジスタに制限されます。
  • LLDB は3種類のウォッチポイントをサポート:変数、式、生のメモリアドレス。
  • 条件付き ウォッチポイントは指定された式が真と評価された場合にのみ作動 — 高頻度の変更をデバッグするために不可欠。
  • オブジェクトのプロパティとコレクション要素のWatchpoints はメモリ再割り当て時に無効になります。
  • Watchpoints はセッション間で保持されず、変数がスコープ外になると削除されます。
  • Watchpoint は、変数は分かっているが変更箇所が不明な場合に最適な選択肢で、特にマルチスレッドコードで有効です。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください