この記事の要点
- Swift には、プログラムの前提条件が実行時に満たされなかった場合にプログラムを終了させる言語機能があります。配列の範囲外アクセス、オプショナルの強制アンラップ、
preconditionなどがその例です。 - Xcode 9.1 より前のデバッガでは、これらによる終了が
EXC_BAD_INSTRUCTIONやEXC_BREAKPOINTといった低レベルな Mach 例外として表示されるだけで、初心者にも熟練者にも分かりにくい原因になっていました。 - Xcode 9.1 では、デバッガ実行中に trap が起きると、原因となった失敗の理由(failure reason)がエディタ上の該当箇所に表示されるよう改善されました。
- この改善は、アプリのエントリポイントが Swift で書かれている場合(
@UIApplicationMain/@NSApplicationMainを付けた app delegate)にのみ有効です。
背景: 前提条件の違反による終了
Swift には、プログラムが満たすべき前提条件を表現する言語機能があり、その前提が実行時に満たされないとプログラムが終了します。たとえば配列への添字アクセスは、添字が範囲内であるという前提を暗黙に表現しています。
// 'index' が 0 未満、または 'array.count - 1' を超える場合、プログラムは終了する。
let element = array[index]
オプショナルの強制アンラップも、失敗時にプログラムを終了させる代表的な操作です。
// 'self.navigationController' が nil の場合、プログラムは終了する。
let nc = self.navigationController!
precondition も同様です。
// 'index' が 0 以下の場合、プログラムは終了する。
precondition(index > 0, "Index must be greater than zero.")
前提条件が誤っていたりコードにバグがあったりした場合、Swift はプログラムが必ず trap することを保証します。特に開発中は、何らかの前提条件が満たされずプログラムが終了し、デバッガがそれを表示する、という状況がよく起こります。
しかし Xcode 9.1 より前のデバッガでは、こうした状況がほかのクラッシュと区別なく ―― 通常は低レベルな Mach 例外である EXC_BAD_INSTRUCTION や EXC_BREAKPOINT として ―― 表示されるだけでした。これが初心者にも熟練者にも混乱の元になっていました。
Xcode 9.1 で何が変わるのか
Xcode 9.1 では、fatal error の表示が大きく改善されました。デバッガ実行中に trap が発生すると、Xcode は trap が起きたエディタ上の該当箇所に 失敗の理由(failure reason) を表示します。
実行時の trap を引き起こす多くのイベントがカバーされており、次のものが含まれます。
nilの強制アンラップ- エラーを生じる forced-try 式(
try!) - 配列の範囲外アクセス
preconditionの失敗assertの失敗fatalErrorの呼び出し
注意点
この改善された表示は、アプリのエントリポイントが Swift で書かれている場合にのみ 利用できます。具体的には、@UIApplicationMain / @NSApplicationMain 属性を付けた app delegate を持つアプリが対象です。