Swift: Swiftの入出力とデバッグ:print、アサーション、ブレークポイント

コードを書くのは仕事の半分です。このレッスンでは、print、アサーション、ブ��ークポイントを使ってコードのエラーを見つけて修正する方法��学びます。

1. 学習目標


2. 実話:独学の初心者

(1) 課題:論理的に正しいコードなのに結果が間違っている

MikeはSwiftを独学する初心者で、平均点を計算するプログラムに取り組んでいます:

SWIFT
let scores = [85, 92, 78, 90, 88]
var total = 0
for i in 0...5 {
    total += scores[i]
}
let average = total / 5
print("Average: \(average)")

プログラムは即座にクラッシュしました。Mikeは理由がわかりませんでした。彼は30分間ネットで検索し、様々な修正を試みました。修正のたびに再実行が必要でしたが、それでも問題を特定できませんでした。

(2) 解決策:printベースのデバッグ

友人がprintを使って中間変数を出力して問題箇所を特定することを勧めました:

SWIFT
let scores = [85, 92, 78, 90, 88]
print("Array length: \(scores.count)")     // Output: 5
var total = 0
for i in 0..<scores.count {               // Use count instead of hardcoding
    print("Processing index \(i): value = \(scores[i])")
    total += scores[i]
}
print("Total: \(total)")
let average = total / 5
print("Average: \(average)")

出力:

TEXT 📖 参照専用
Array length: 5
Processing index 0: value = 85
Processing index 1: value = 92
Processing index 2: value = 78
Processing index 3: value = 90
Processing index 4: value = 88
Total: 433
Average: 86

中間変数を出力することで、Mikeは配列のインデックスが0-4であるのに、0...5(インデックス5を含む)を使っていたことを即座に発見しました。

(3) 結果:printを習得した後のデバッグ効率

観点 Before(推測) After(printデバッグ)
問題特定までの時間 30分以上 1〜2分
修正の正確さ 50%(しばしば間違い) 95%
コードの理解 「なぜクラッシュしたかわからない」 「各ステップの状態がわかる」
新��い問題を解決する自信 3/10 8/10

3. printの高度な使い方

printはSwiftで最もよく使われるデバッグツールですが、そのパラメータは値を出力するだけではありません。

100%
graph TB
    A[print関数] --> B["items: 出力する値"]
    A --> C["separator: 区切り文字"]
    A --> D["terminator: 終端文字"]
    A --> E["to: 出力先"]
    B --> F[カンマ区切りで複数の値]
    C --> G["デフォルト: スペース"]
    C --> H["カスタム: | や , など"]
    D --> I["デフォルト: 改行"]
    D --> J["カスタム: 空文字列"]
パラメータ デフォルト 目的
items Any... 必須 出力する内容
separator String " " 複数項目間の区切り文字
terminator String "\n" 末尾の改行文字
to TextOutputStream nil 出力先(デフォルト: コンソール)

(1) separatorとterminator

SWIFT
// Default: space-separated, newline-terminated
print("Hello", "Swift", "World")
// Hello Swift World
// Custom separator
print("Hello", "Swift", "World", separator: ", ")
// Hello, Swift, World
// Custom terminator (no newline)
print("Loading", terminator: "...")
print("Done")
// Loading...Done
// Combined usage
print("A", "B", "C", separator: " | ", terminator: ".\n")
// A | B | C.

(2) debugPrintとdump

debugPrintはデバッグ情報(引用符と型情報付き)を出力し、dumpは詳細な構造を出力します:

SWIFT
let name = "Alice"
let numbers = [1, 2, 3]
print(name)          // Alice
debugPrint(name)     // "Alice"
dump(name)           // - "Alice"
print(numbers)       // [1, 2, 3]
debugPrint(numbers)  // [1, 2, 3]
dump(numbers)
// ▿ 3 elements
//   - 0 : 1
//   - 1 : 2
//   - 2 : 3
関数 目的 文字列出力 配列出力
print 通常出力 Alice [1, 2, 3]
debugPrint デバッグ出力(型情報を表示) "Alice" [1, 2, 3]
dump 詳細構造出力 - "Alice" 1要素ずつ表示

▶ サンプル: 整形されたログ出力

SWIFT
// ============================================
// Simulate system log output
// Demonstrates advanced print parameters and debugPrint
// ============================================
let event = "USER_LOGIN"
let user = "Alice"
let statusCode = 200
let duration = 0.045
// 1. Use separator to format the log
print("[\(event)]", user, "Status: \(statusCode)", separator: " | ", terminator: "")
print(" (\(duration)s)")
// [USER_LOGIN] | Alice | Status: 200 (0.045s)
// 2. Output tabular data
print()
print("=== Report ===")
print("Item", "Price", "Qty", separator: " | ")
print("-----", "-----", "---", separator: " | ")
print("Book", "12.99", "3", separator: " | ")
print("Pen", "1.50", "10", separator: " | ")
print("Bag", "49.99", "1", separator: " | ")
// 3. debugPrint for development debugging
let input: String? = "test"
debugPrint("Debug: input = \(input)")
// "Debug: input = Optional(\"test\")"

出力:

TEXT 📖 参照専用
[USER_LOGIN] | Alice | ステータス: 200 (0.045s)

=== レポート ===
商品 | 価格 | 数量
----- | ----- | ---
本 | 12.99 | 3
ペン | 1.50 | 10
バッグ | 49.99 | 1
デバッグ: input = Optional("test")

4. アサーションとプレコンディション

アサーションとプレコンディションはSwiftの組み込み防御的プログラミ���グツールで、開発中にロジックエラーを早期に発見します。

100%
graph LR
    A[実行時チェック] --> B[assert]
    A --> C[precondition]
    B --> D[デバッグモードのみ]
    B --> E[開発中の問題を発見]
    C --> F[デバッグ + リリース]
    C --> G[回復不能なエラー]
関数 有効モード 目的
assert デバッグのみ 開発中の内部整合性チェック assert(age > 0)
assertionFailure デバッグのみ 無条件アサーション発動 assertionFailure("ここに到達すべきではない")
precondition 全モード 事前条件チェック precondition(!name.isEmpty)
preconditionFailure 全モード 無条件終了 preconditionFailure("致命的エラー")

(1) assert デバッグアサーション

assertはデバッグモードでのみ有効で、リリースモードでは削除されるため、パフォーマンスへの影響はありません:

SWIFT
func calculateDiscount(price: Double, percent: Double) -> Double {
    assert(price > 0, "Price must be greater than 0")
    assert(percent >= 0 && percent <= 100, "Discount must be between 0-100")
    let discount = price * percent / 100.0
    return price - discount
}
let finalPrice = calculateDiscount(price: 100.0, percent: 20)
print(finalPrice)  // 80.0
// The following would trigger assertion failures in Debug mode:
// calculateDiscount(price: -10, percent: 20)  // ❌ assert fails
// calculateDiscount(price: 100, percent: 150) // ❌ assert fails

(2) preconditionチェック

preconditionはデバッグモードとリリースモードの両方で有効で、条件が満たせない場合に��ログラムを即座に終了します:

SWIFT
func sendEmail(to address: String, message: String) {
    precondition(address.contains("@"), "Invalid email address: \(address)")
    precondition(!message.isEmpty, "Message cannot be empty")
    print("Sending email to \(address): \(message)")
}
sendEmail(to: "alice@example.com", message: "Hello!")
// Sending email to alice@example.com: Hello!
// The following would trigger precondition failures (all modes):
// sendEmail(to: "invalid", message: "Hi")
💡 ヒント: assertは「内部チェック」(自分のコードが正しいことを確認)、preconditionは「外部契約」(呼び出し側が要件を満たすことを確認)に使用します。デバッグ中はassertを多用し、公開APIの境界にはpreconditionを使用します。

▶ サンプル: パラメータ検証

SWIFT
// ============================================
// User registration parameter checks
// Demonstrates assert and precondition usage
// ============================================
import Foundation
func registerUser(name: String, age: Int, email: String) {
    // precondition: Public API contract (active in all modes)
    precondition(name.count >= 2, "Username must be at least 2 characters")
    precondition(age >= 18, "User must be at least 18 years old")
    precondition(email.contains("@"), "Invalid email format")
    // assert: Internal logic check (Debug only)
    let emailParts = email.split(separator: "@")
    assert(emailParts.count == 2, "Email should contain exactly one @ symbol")
    let domain = String(emailParts[1])
    assert(domain.contains("."), "Email domain is invalid")
    // Actual registration logic
    print("Registration successful: \(name), age \(age)")
    print("Confirmation email sent to: \(email)")
}
// Valid call
registerUser(name: "Alice", age: 28, email: "alice@example.com")
print("---")
// Calls that would trigger precondition failures (commented out to avoid crashing)
// registerUser(name: "A", age: 20, email: "test@test.com")

出力:

TEXT 📖 参照専用
Registration successful: Alice, age 28
Confirmation email sent to: alice@example.com
---

5. Playgroundデバッグ

Playgroundsはprintよりも強力なデバッグ機能を提供し、ライブプレビューとブレークポイントを含みます。

(1) Playgroundライブプレビュー

Playgroundsのサイドバーには各行の結果がリアルタイムで表示されます:

SWIFT
// Run in Playground — the sidebar shows each step's result
let name = "Alice"    // "Alice"
var score = 0         // 0
score += 85           // 85
score += 92           // 177
let average = score / 2  // 88
100%
graph TB
    A[Playgroundデバッグ] --> B[ライブ結果パネル]
    A --> C[ブレークポイントデバッグ]
    A --> D[値の履歴]
    B --> E[行ごとに結果を表示]
    C --> F[一時停止 / ステップ実行 / 続行]
    D --> G[変数値の変化曲線]
機能 使用方法 目的
ライブプレビュー コード編集時に自動で右側に結果表示 各ステップの結果を素早く確認
ブレークポイント 行番号をクリックして追加 実行を一時停止し、ステップ実行で追跡
値の履歴 変数にマウスオーバー 変数が時間とともにどう変化するか確認
式のプレビュー コードを選択 選択した式を素早く評価

(2) ブレークポイントデバッグ

行番号の左側をクリックしてブレークポイ��トを設定します。プログラムがその行に到達すると一時停止し、現在のすべての変数値を検査できます:

SWIFT
func calculateTotal(items: [Double], tax: Double) -> Double {
    var subtotal = 0.0
    // Set a breakpoint on this line
    for item in items {
        subtotal += item
    }
    let taxAmount = subtotal * tax
    let total = subtotal + taxAmount
    return total
}
let cart = [29.99, 49.99, 15.00]
let final = calculateTotal(items: cart, tax: 0.08)
print("Total: $\(final)")
💡 ヒント: XcodeやPlaygroundsで、行番号をクリックしてブレークポイントを追加します。トリガーされると、下部のデバッグエリアに現在のすべての変数値が表示されます。「続行」または「ステップオーバー」を選択して進むことができます。

▶ サンプル: Playgroundデバッグの実践

SWIFT
// ============================================
// Practice Playground debugging techniques
// Paste this code into a Playground and run it
// ============================================
import Foundation
// 1. Set a breakpoint on the line below to observe variable values
let data: [String: Any] = [
    "product": "Swift Book",
    "price": 39.99,
    "quantity": 3,
    "inStock": true
]
// 2. Step through the following code
let productName = data["product"] as? String ?? "Unknown"
let price = data["price"] as? Double ?? 0.0
let quantity = data["quantity"] as? Int ?? 0
let inStock = data["inStock"] as? Bool ?? false
print("Product: \(productName)")
print("Price: $\(price)")
print("Quantity: \(quantity)")
print("In Stock: \(inStock)")
// 3. Observe the conditional check results
if inStock {
    let totalCost = price * Double(quantity)
    print("Total Cost: $\(totalCost)")
} else {
    print("Item is out of stock")
}
// 4. Use dump to inspect complex data
print("\n=== Debug Info ===")
dump(data)

出力:

TEXT 📖 参照専用
商品: Swift Book
価格: $39.99
数量: 3
在庫あり: true
合計コスト: $119.97

=== デバッグ情報 ===
▿ 4 key/value pairs
  ▿ (2 elements)
    - key: "product"
    - value: "Swift Book"
  ▿ (2 elements)
    - key: "price"
    - value: 39.99
  ▿ (2 elements)
    - key: "quantity"
    - value: 3
  ▿ (2 elements)
    - key: "inStock"
    - value: true

6. 完全な例:成績分析デバッグツール

SWIFT
// ============================================
// Grade analysis tool
// Combines print debugging + assertion checks + formatted output
// ============================================
import Foundation
// 1. Student grade data
let studentName = "Alice"
let scores = [85.0, 92.0, 78.0, 90.0, 88.0]
// 2. Debug assertions to validate data
assert(scores.count > 0, "Score list cannot be empty")
for score in scores {
    assert(score >= 0 && score <= 100, "Score must be between 0-100: \(score)")
}
// 3. Calculate statistics
var total = 0.0
var highest = scores[0]
var lowest = scores[0]
// Set a breakpoint here to observe the loop process
for (index, score) in scores.enumerated() {
    print("[DEBUG] Index \(index): \(score)")
    total += score
    if score > highest { highest = score }
    if score < lowest { lowest = score }
}
let average = total / Double(scores.count)
// 4. Formatted output
print(String(repeating: "=", count: 35))
print("Student: \(studentName)")
print(String(repeating: "-", count: 35))
print("Subject", "Score", "Grade", separator: " | ")
print(String(repeating: "-", count: 35))
for (index, score) in scores.enumerated() {
    let grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"
    print("Subject \(index + 1)", score, grade, separator: " | ")
}
print(String(repeating: "-", count: 35))
print("Total: \(total)")
print("Average: \(String(format: "%.1f", average))")
print("Highest: \(highest)")
print("Lowest: \(lowest)")
print(String(repeating: "=", count: 35))
// 5. Precondition to ensure reasonable results
precondition(average >= 0 && average <= 100, "Average is outside expected range")
precondition(highest >= lowest, "Highest score should not be lower than lowest")

出力:

TEXT 📖 参照専用
[DEBUG] Index 0: 85.0
[DEBUG] Index 1: 92.0
[DEBUG] Index 2: 78.0
[DEBUG] Index 3: 90.0
[DEBUG] Index 4: 88.0
===================================
Student: Alice
-----------------------------------
Subject | Score | Grade
-----------------------------------
Subject 1 | 85.0 | B
Subject 2 | 92.0 | A
Subject 3 | 78.0 | C
Subject 4 | 90.0 | A
Subject 5 | 88.0 | B
-----------------------------------
Total: 433.0
Average: 86.6
Highest: 92.0
Lowest: 78.0
===================================

❓ よくある質問

Q printとdebugPrintの実際の違いは何ですか?
A printは人間が読みやすい形式(例:Alice)で出力し、debugPrintはデバッグ向け形式(例:引用符付きで"Alice")で出力します。カスタム型はCustomStringConvertibleCustomDebugStringConvertibleプロトコルを実装して両方の出力を制御できます。
Q assertはリリースモードで削除されますか?
A はい。assertはデバッグモード(-Onone)でのみ有効です。リリースモードでは、コンパイラはassertの評価と実行をスキップします。したがって、assertの内部に副作用のあるロジックを決して置かないでください。
Q preconditionをassertの代わりに使用すべきなのはどんな時ですか?
A preconditionは全モードで有効で、回復不能な致命的エラーに使用します。例:配列境界チェック前、必須パラメータがnil、論理的に決して到達しないはずの分岐。公開APIのパラメータ検証にはpreconditionを使用します。
Q Playgroundsでブレークポイントを使うには?
A 行番号の左側をクリックしてブレークポイントを設定します��プログラムがブレークポイントに到達すると一時停止します。任意の変数にマウスオーバーすると値が表示されます。続行ボタンまたはF6を押して次の行にステップ実行します。
Q printを使ったのにコン��ールに出力が表示されないのはなぜですか?
A Playgroundで「.enableResults」モードが有効かどうかを確認してください。Xcodeでは、デバッグエリアが開いていることを確認します(Shift+Cmd+Y)。Swift Playgroundsアプリでは、出力は下部パネルに表示されます。

📖 まとめ


📝 練習問題

  1. 初級: printを使って簡単な九九の表(1-3)を生成し、separatorterminatorで形式を制御して表スタイルで出力してください。
  2. 中級: 関数divide(_ a: Double, by b: Double) -> Doubleを作成し、preconditionで除数が0でないことをチェックし、assertで結果が妥当な範囲内かを確認してください。そしてprintで整形して結果を出力してください。
  3. 上級: ATM出金プログラムをシミュレートしてください。assertで出金額が100の倍数であることを確認し、preconditionで残高が十分であることをチェックします。printseparatorterminatorを使って、時間、金額、残高を含む取引詳細を整形して表示してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%