Swift チートシート

変数と定数

データの入れ物を定義します。

目的構文説明
変数宣言var 名前: 型 = 初期値後から値を変更できる変数を宣言します。型推論が可能な場合は型を省略できます。
var message: String = "こんにちは"
var score = 0 // Int型と推論される
message = "さようなら"
score += 10
定数宣言let 名前: 型 = 初期値一度値を設定すると変更できない定数を宣言します。型推論が可能な場合は型を省略できます。可能な限り let を使うことが推奨されます。
let pi: Double = 3.14159
let defaultName = "ゲスト" // String型と推論される
// pi = 3.14 // エラー!定数は変更不可
型指定var/let 名前: 型明示的に変数や定数の型を指定します。初期値を後で設定する場合などに必要です。
let maximumLoginAttempts: Int
// ... 何らかの処理 ...
maximumLoginAttempts = 3
var environment: String
environment = "development"
タプル(値1, 値2, ...)
(名前1: 値1, 名前2: 値2, ...)
複数の値をまとめて一時的に扱うための複合型です。要素に名前をつけることも可能です。
let httpStatus = (404, "Not Found")
print("ステータスコード: \(httpStatus.0)") // 404
print("メッセージ: \(httpStatus.1)") // Not Found
let userInfo = (name: "田中", age: 30, isAdmin: false)
print("名前: \(userInfo.name)") // 田中
print("年齢: \(userInfo.age)") // 30

オプショナル型 (?)

値が存在しない可能性 (nil) を表現するための型です。安全な値の取り扱いに不可欠です。

目的構文説明
宣言var/let 名前: 型?
var/let 名前: 型!
? をつけるとオプショナル型になります。初期値は nil です。! は暗黙的アンラップオプショナルで、アクセス時に自動でアンラップされますが、nil だとクラッシュするため注意が必要です。
var optionalString: String? = "値があるかも"
var nickname: String? // 初期値は nil
var definitelyString: String! = "必ず値があるはず"
// print(definitelyString) // アクセス時に自動アンラップ
Optional Binding (if let / guard let)if let 定数名 = オプショナル変数 { ... }
guard let 定数名 = オプショナル変数 else { ... }
オプショナル変数が nil でない場合に、アンラップされた値を定数に束縛して安全に利用します。guardnil の場合に早期リターンするのに便利です。
var middleName: String? = nil
if let name = middleName { print("ミドルネーム: \(name)") // middleNameがnilでない場合のみ実行
} else { print("ミドルネームはありません")
}
func printFullName(firstName: String, middleName: String?, lastName: String) { guard let middle = middleName else { print("\(firstName) \(lastName)") return // middleNameがnilならここで処理終了 } print("\(firstName) \(middle) \(lastName)")
}
Nil-Coalescing Operator (??)オプショナル変数 ?? デフォルト値オプショナル変数が nil の場合に、?? の右辺のデフォルト値を使用します。
let userColorPreference: String? = nil
let colorToUse = userColorPreference ?? "blue"
print(colorToUse) // "blue"
let actualAge: Int? = 25
let displayAge = actualAge ?? 99
print(displayAge) // 25
Optional Chaining (?)オプショナル変数?.プロパティ
オプショナル変数?.メソッド()
オプショナル変数が nil でない場合にのみ、プロパティアクセスやメソッド呼び出しを実行します。結果もオプショナル型になります。
struct Person { var residence: Residence?
}
struct Residence { var address: String
}
let person: Person? = Person(residence: Residence(address: "東京都"))
// let person: Person? = nil // こちらで試すと結果が変わる
// personがnilでなく、residenceもnilでない場合のみaddressを取得
let address = person?.residence?.address
if let addr = address { print("住所: \(addr)")
} else { print("住所は不明です")
}
// メソッド呼び出し
let maybeCount = person?.residence?.address.count
print(maybeCount) // Optional(3) または nil
強制アンラップ (!)オプショナル変数!オプショナル変数の値を強制的に取り出します。注意: もし変数が nil の場合、実行時エラーでクラッシュします。 使用は確実に nil でないと分かっている場合に限定すべきです。
let guaranteedValue: String? = "値あり"
let unwrappedValue = guaranteedValue! // "値あり"
let nilValue: Int? = nil
// let criticalError = nilValue! // ここでクラッシュ!

コレクション型

複数の値をまとめて管理するための型です。

配列 (Array)

順序付けられた値のコレクションです。

目的構文説明
宣言・初期化var/let 名前: [型] = [値1, 値2, ...]
var/let 名前 = [値1, 値2, ...]
var/let 名前: [型] = []
var/let 名前 = [型]()
特定の型の要素を格納する配列を宣言・初期化します。
var numbers: [Int] = [1, 2, 3]
let fruits = ["apple", "banana", "cherry"] // [String]と推論
var emptyDoubles: [Double] = []
var emptyStrings = [String]()
要素へのアクセス配列[インデックス]指定したインデックス(0から始まる)の要素にアクセスします。範囲外アクセスはエラーになります。
let fruits = ["apple", "banana", "cherry"]
let firstFruit = fruits[0] // "apple"
// let outOfBounds = fruits[3] // エラー!
要素の追加配列.append(要素)
配列 += [要素1, 要素2]
配列の末尾に要素を追加します。var で宣言された配列のみ変更可能です。
var colors = ["red", "green"]
colors.append("blue") // ["red", "green", "blue"]
colors += ["yellow", "orange"] // ["red", "green", "blue", "yellow", "orange"]
要素の挿入配列.insert(要素, at: インデックス)指定したインデックスに要素を挿入します。
var letters = ["a", "c", "d"]
letters.insert("b", at: 1) // ["a", "b", "c", "d"]
要素の削除配列.remove(at: インデックス)
配列.removeLast()
配列.removeAll()
指定したインデックスの要素、最後の要素、または全ての要素を削除します。
var numbers = [10, 20, 30, 40, 50]
let removedElement = numbers.remove(at: 1) // 20が削除され、返される
// numbers は [10, 30, 40, 50]
numbers.removeLast() // 50が削除される
// numbers は [10, 30, 40]
numbers.removeAll() // [] (空になる)
要素数配列.count配列に含まれる要素の数を取得します。
let tasks = ["掃除", "洗濯", "買い物"]
print("タスクの数: \(tasks.count)") // 3
空かどうか配列.isEmpty配列が空かどうかをBool値で返します。count == 0 より効率的です。
let emptyArray: [Int] = []
if emptyArray.isEmpty { print("配列は空です")
}
繰り返し処理 (for-in)for 要素 in 配列 { ... }配列の各要素に対して処理を繰り返します。
let animals = ["dog", "cat", "rabbit"]
for animal in animals { print("Hello, \(animal)!")
}
高階関数 (map)配列.map { クロージャ }各要素に指定した変換処理を適用し、新しい配列を生成します。
let numbers = [1, 2, 3, 4]
let squaredNumbers = numbers.map { $0 * $0 }
// squaredNumbers は [1, 4, 9, 16]
let names = ["Alice", "Bob", "Charlie"]
let greetings = names.map { "こんにちは、\($0)さん!" }
// greetings は ["こんにちは、Aliceさん!", "こんにちは、Bobさん!", "こんにちは、Charlieさん!"]
高階関数 (filter)配列.filter { クロージャ }クロージャが `true` を返す要素だけを含む新しい配列を生成します。
let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
// evenNumbers は [2, 4, 6]
let scores = [75, 40, 92, 88, 55]
let passingScores = scores.filter { $0 >= 60 }
// passingScores は [75, 92, 88]
高階関数 (reduce)配列.reduce(初期値) { 結果, 要素 in ... }要素を畳み込み、単一の値を生成します(合計、連結など)。
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 } // 0 + 1 + 2 + 3 + 4 + 5
// sum は 15
let words = ["Swift", "is", "fun"]
let sentence = words.reduce("") { $0 + ($0.isEmpty ? "" : " ") + $1 }
// sentence は "Swift is fun"

辞書 (Dictionary)

キーと値のペアを格納する、順序のないコレクションです。

目的構文説明
宣言・初期化var/let 名前: [キー型: 値型] = [キー1: 値1, ...]
var/let 名前 = [キー1: 値1, ...]
var/let 名前: [キー型: 値型] = [:]
var/let 名前 = [キー型: 値型]()
キーと値のペアを格納する辞書を宣言・初期化します。キーはHashableプロトコルに準拠している必要があります (String, Intなど)。
var airports: [String: String] = ["HND": "Haneda", "NRT": "Narita"]
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] // [String: Int]と推論
var emptyScores: [String: Int] = [:]
var emptyResponses = [Int: String]()
値へのアクセス辞書[キー]指定したキーに対応する値にアクセスします。キーが存在しない場合 nil が返るため、結果はオプショナル型になります。
let airports = ["HND": "Haneda", "NRT": "Narita"]
if let naritaName = airports["NRT"] { print("NRTの名称: \(naritaName)") // Narita
} else { print("NRTのキーが見つかりません")
}
print(airports["KIX"]) // nil (存在しないキー)
値の追加・更新辞書[キー] = 値指定したキーに値を設定します。キーが既に存在すれば値が更新され、存在しなければ新しいキーと値のペアが追加されます。var で宣言された辞書のみ変更可能です。
var userAges: [String: Int] = ["Alice": 30]
userAges["Bob"] = 25 // 追加: ["Alice": 30, "Bob": 25]
userAges["Alice"] = 31 // 更新: ["Alice": 31, "Bob": 25]
値の削除辞書[キー] = nil
辞書.removeValue(forKey: キー)
指定したキーとその値を削除します。removeValue(forKey:) は削除された値をオプショナル型で返します。
var capitals = ["Japan": "Tokyo", "USA": "Washington D.C."]
capitals["USA"] = nil // ["Japan": "Tokyo"]
let removedValue = capitals.removeValue(forKey: "Japan") // Optional("Tokyo")
// capitals は [:] (空になる)
要素数辞書.count辞書に含まれるキーと値のペアの数を取得します。
let parameters = ["page": 1, "limit": 20]
print("パラメータ数: \(parameters.count)") // 2
空かどうか辞書.isEmpty辞書が空かどうかをBool値で返します。
let emptyDict: [String: String] = [:]
if emptyDict.isEmpty { print("辞書は空です")
}
繰り返し処理 (for-in)for (キー, 値) in 辞書 { ... }辞書の各キーと値のペアに対して処理を繰り返します。処理される順序は保証されません。
let colors = ["red": "#FF0000", "green": "#00FF00", "blue": "#0000FF"]
for (name, hex) in colors { print("\(name) のカラーコードは \(hex) です")
}
キーのみ/値のみ取得Array(辞書.keys)
Array(辞書.values)
辞書のキーのみ、または値のみを配列として取得します。
let permissions = ["admin": true, "editor": true, "viewer": false]
let roles = Array(permissions.keys) // ["admin", "editor", "viewer"] (順序不定)
let accessLevels = Array(permissions.values) // [true, true, false] (順序不定)

セット (Set)

順序がなく、重複する値を持たないコレクションです。集合演算に便利です。

目的構文説明
宣言・初期化var/let 名前: Set<型> = [値1, 値2, ...]
var/let 名前: Set = [値1, 値2, ...]
var/let 名前 = Set<型>()
特定の型の要素を格納するセットを宣言・初期化します。要素の型はHashableプロトコルに準拠している必要があります。配列リテラルから初期化できますが、型を明示する必要があります。
var oddNumbers: Set<Int> = [1, 3, 5, 7, 9, 1] // 重複は無視される
// oddNumbers は {1, 3, 5, 7, 9} (順序不定)
let favoriteGenres: Set = ["Rock", "Jazz", "Classical"]
var emptyTags = Set<String>()
要素の追加セット.insert(要素)セットに要素を追加します。既に要素が存在する場合は何も起こりません。var で宣言されたセットのみ変更可能です。
var visitedCities: Set = ["Tokyo", "Osaka"]
visitedCities.insert("Kyoto") // {"Tokyo", "Osaka", "Kyoto"}
visitedCities.insert("Tokyo") // 変化なし {"Tokyo", "Osaka", "Kyoto"}
要素の削除セット.remove(要素)
セット.removeAll()
指定した要素、または全ての要素を削除します。remove() は削除された要素をオプショナル型で返します(存在しなければ nil)。
var ingredients: Set = ["flour", "sugar", "egg"]
let removed = ingredients.remove("sugar") // Optional("sugar")
// ingredients は {"flour", "egg"}
ingredients.removeAll() // {} (空になる)
要素の存在確認セット.contains(要素)セットに指定した要素が含まれているかどうかをBool値で返します。配列よりも高速です。
let allowedUsers: Set = ["user1", "admin", "guest"]
if allowedUsers.contains("admin") { print("管理者アクセスを許可します")
}
要素数セット.countセットに含まれる要素の数を取得します。
let primeNumbers: Set = [2, 3, 5, 7, 11]
print("素数の数: \(primeNumbers.count)") // 5
空かどうかセット.isEmptyセットが空かどうかをBool値で返します。
let emptySet = Set<Int>()
if emptySet.isEmpty { print("セットは空です")
}
繰り返し処理 (for-in)for 要素 in セット { ... }セットの各要素に対して処理を繰り返します。処理される順序は保証されません。
let vowels: Set = ["a", "e", "i", "o", "u"]
for vowel in vowels { print("\(vowel) is a vowel.")
}
集合演算 (和集合)セット1.union(セット2)両方のセットに含まれる全ての要素からなる新しいセットを生成します。
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]
let unionSet = setA.union(setB) // {1, 2, 3, 4, 5}
集合演算 (積集合)セット1.intersection(セット2)両方のセットに共通して含まれる要素からなる新しいセットを生成します。
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]
let intersectionSet = setA.intersection(setB) // {3}
集合演算 (差集合)セット1.subtracting(セット2)セット1に含まれ、かつセット2に含まれない要素からなる新しいセットを生成します。
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]
let subtractingSet = setA.subtracting(setB) // {1, 2}
集合演算 (対称差集合)セット1.symmetricDifference(セット2)どちらか一方のセットにのみ含まれる要素からなる新しいセットを生成します。
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]
let symmetricDiffSet = setA.symmetricDifference(setB) // {1, 2, 4, 5}
集合の比較 (isSubset)セット1.isSubset(of: セット2)セット1がセット2の部分集合であるか(セット1の全要素がセット2に含まれるか)を返します。
let setA: Set = [1, 2]
let setB: Set = [1, 2, 3]
print(setA.isSubset(of: setB)) // true
集合の比較 (isSuperset)セット1.isSuperset(of: セット2)セット1がセット2のスーパーセットであるか(セット1がセット2の全要素を含むか)を返します。
let setA: Set = [1, 2, 3]
let setB: Set = [1, 2]
print(setA.isSuperset(of: setB)) // true
集合の比較 (isDisjoint)セット1.isDisjoint(with: セット2)セット1とセット2が互いに素であるか(共通の要素を持たないか)を返します。
let setA: Set = [1, 2]
let setB: Set = [3, 4]
print(setA.isDisjoint(with: setB)) // true

制御構文

プログラムの実行フローを制御します。

種別構文説明
if文if 条件 { ... } else if 条件 { ... } else { ... }条件が真 (true) の場合に特定のコードブロックを実行します。複数の条件分岐や、いずれの条件にも合致しない場合の処理も記述できます。
let temperature = 25
if temperature > 30 { print("暑い!")
} else if temperature < 10 { print("寒い!")
} else { print("快適です")
}
guard文guard 条件 else { 早期リターン処理; return/throw/break/continue }
後続の処理 (条件が真の場合)
条件が偽 (false) の場合に、特定のコードブロック(通常は関数の早期リターン)を実行します。条件が真の場合は、guard文以降の処理が続行されます。オプショナルバインディングと組み合わせて使うことが多いです。
func process(data: Data?) { guard let validData = data else { print("エラー: データがありません") return // データがnilならここで関数終了 } // validData はここでアンラップされた状態で安全に使える print("データ処理中: \(validData.count) バイト")
}
switch文switch 評価する値 { case パターン1: ... case パターン2 where 条件: ... default: ... }評価する値が特定のパターンに一致する場合に、対応するコードブロックを実行します。Swiftのswitch文は強力で、タプル、範囲、値束縛、where句など多様なパターンマッチングが可能です。網羅的である必要がありdefault ケースが不要な場合もあります(enumなど)。
// 基本的な使い方
let signal = "red"
switch signal {
case "red": print("止まれ")
case "yellow": print("注意")
case "green": print("進め")
default: print("不明な信号")
}
// 値束縛とwhere句
let point = (1, -1)
switch point {
case let (x, y) where x == y: print("(\(x), \(y)) は x = y の線上")
case let (x, y) where x == -y: print("(\(x), \(y)) は x = -y の線上") // ここにマッチ
case let (x, y): print("(\(x), \(y)) はその他の点")
}
// 範囲
let score = 85
switch score {
case 90...100: print("優")
case 60..<90: // 60以上90未満 print("良") // ここにマッチ
default: print("可")
}
for-in ループfor item in コレクション { ... }
for i in 範囲 { ... }
コレクション(配列、辞書、セット、範囲など)の各要素に対して処理を繰り返します。
let numbers = [1, 2, 3]
for number in numbers { print(number) // 1, 2, 3 が順に出力
}
for i in 1...5 { // 1から5までの範囲 (5を含む) print(i) // 1, 2, 3, 4, 5
}
for _ in 0..<3 { // 0から3未満の範囲 (3を含まない)、要素は不要 print("ループ") // 3回出力
}
let person = ["name": "Alice", "age": "30"]
for (key, value) in person { print("\(key): \(value)") // 順序不定で出力
}
while ループwhile 条件 { ... }条件が真である間、コードブロックを繰り返し実行します。ループに入る前に条件が評価されます。
var count = 0
while count < 3 { print("カウント: \(count)") // 0, 1, 2 count += 1
}
repeat-while ループrepeat { ... } while 条件コードブロックを最低1回実行し、その後、条件が真である間、繰り返し実行します。ループの最後に条件が評価されます。
var dice: Int
repeat { dice = Int.random(in: 1...6) print("出た目: \(dice)")
} while dice != 6 // 6が出るまで繰り返す
breakbreak現在のループ(for, while, repeat-while)またはswitch文から即座に抜け出します。
let numbers = [1, 5, -2, 8, 3]
for number in numbers { if number < 0 { print("負の数が見つかったので中断します") break // ループを終了 } print(number) // 1, 5 が出力される
}
continuecontinue現在のループの反復処理を中断し、次の反復処理を開始します。
let numbers = [1, -2, 3, -4, 5]
var sum = 0
for number in numbers { if number < 0 { continue // 負の数はスキップして次の要素へ } sum += number
}
print("正の数の合計: \(sum)") // 1 + 3 + 5 = 9
fallthroughfallthroughswitch文のcaseブロック内で使用し、そのcaseの実行が完了した後、次のcaseブロック(通常は実行されない)へ制御を移します。C言語スタイルの動作が必要な稀なケースで使用されます。
let value = 5
switch value {
case 5: print("5です") fallthrough // 次のcaseも実行する
case 4...6: print("4から6の範囲です") // この行も実行される
default: print("範囲外です")
}

関数とクロージャ

特定のタスクを実行するコードのまとまりです。

関数 (Function)

目的構文説明
基本定義func 関数名(引数ラベル パラメータ名: 型, ...) -> 戻り値の型 { ... return 戻り値 }名前、引数、戻り値を持つ関数を定義します。引数ラベルは関数呼び出し時に使用し、パラメータ名は関数内部で使用します。戻り値がない場合は -> Void または省略可能です。
func greet(person name: String) -> String { let greeting = "こんにちは、" + name + "さん!" return greeting
}
let message = greet(person: "鈴木") // "こんにちは、鈴木さん!"
func printHelp() { // 戻り値なし print("ヘルプメッセージを表示します。")
}
printHelp()
引数ラベル省略func 関数名(_ パラメータ名: 型) { ... }引数ラベルを _ にすると、関数呼び出し時にラベルを省略できます。
func add(_ a: Int, to b: Int) -> Int { return a + b
}
let sum = add(5, to: 3) // 8 (最初の引数ラベルは省略)
デフォルト引数値func 関数名(パラメータ名: 型 = デフォルト値) { ... }引数にデフォルト値を設定できます。呼び出し時にその引数を省略するとデフォルト値が使用されます。
func power(base: Int, exponent: Int = 2) -> Int { var result = 1 for _ in 0..<exponent { result *= base } return result
}
let square = power(base: 3) // exponentはデフォルトの2が使われる -> 9
let cube = power(base: 3, exponent: 3) // exponentを明示的に指定 -> 27
可変長引数func 関数名(パラメータ名: 型...) { ... }... をつけると、同じ型の引数を0個以上受け取れます。関数内では配列として扱われます。
func average(_ numbers: Double...) -> Double { if numbers.isEmpty { return 0.0 } var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count)
}
let avg1 = average(1.0, 2.0, 3.0) // 2.0
let avg2 = average(5.5, 10.5) // 8.0
let avg3 = average() // 0.0
inoutパラメータfunc 関数名(引数ラベル パラメータ名: inout 型) { ... }
関数呼び出し: 関数名(引数ラベル: &変数)
関数の内部で引数として渡された変数の値を直接変更できるようにします。呼び出し時には変数名の前に & をつけます。値型(構造体、enumなど)の変数を関数内で変更したい場合に使用します。
func swapValues(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA
}
var x = 10
var y = 20
swapValues(&x, &y) // & をつけて渡す
print("x: \(x), y: \(y)") // x: 20, y: 10
関数型(引数の型) -> 戻り値の型関数の型自体を変数や定数に代入したり、別の関数の引数や戻り値として使用できます。
func addTwoInts(_ a: Int, _ b: Int) -> Int { a + b }
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int { a * b }
var mathFunction: (Int, Int) -> Int // 関数型の変数を宣言
mathFunction = addTwoInts // 加算関数を代入
print("結果: \(mathFunction(2, 3))") // 5
mathFunction = multiplyTwoInts // 乗算関数を代入
print("結果: \(mathFunction(2, 3))") // 6
// 関数を引数として受け取る関数
func printMathResult(_ mathFunc: (Int, Int) -> Int, _ a: Int, _ b: Int) { print("計算結果: \(mathFunc(a, b))")
}
printMathResult(addTwoInts, 5, 4) // 計算結果: 9

クロージャ (Closure)

自己完結型のコードブロックで、変数や定数に代入したり、関数の引数として渡したりできます。関数は名前付きクロージャの一種です。

目的構文説明
基本構文{ (パラメータ) -> 戻り値の型 in 実行コード }中括弧 {} で囲まれたコードブロックです。パラメータ、戻り値の型、in キーワードで本体と区切られます。型推論が可能な場合は、多くを省略できます。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
// 通常の関数としてソート
func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2
}
var reversedNamesFunc = names.sorted(by: backward) // ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
// クロージャ式としてソート
var reversedNamesClosure = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2
})
// 型推論を利用して省略
reversedNamesClosure = names.sorted(by: { s1, s2 in return s1 > s2 } )
// 単一式クロージャの暗黙的なリターン
reversedNamesClosure = names.sorted(by: { s1, s2 in s1 > s2 } )
// 引数名の省略 ($0, $1, ...)
reversedNamesClosure = names.sorted(by: { $0 > $1 } )
// 演算子メソッド (Stringの > 演算子が(String, String) -> Bool に適合するため)
reversedNamesClosure = names.sorted(by: >)
後置クロージャ (Trailing Closure)関数名() { クロージャ本体 }
関数名(引数) { クロージャ本体 }
関数の最後の引数がクロージャである場合、関数呼び出しの括弧 () の外にクロージャを記述できます。可読性が向上します。最後の引数以外にもクロージャがある場合でも、最後の引数であれば使えます。
let numbers = [1, 2, 3, 4]
// map関数の最後の引数はクロージャ
let doubledNumbers = numbers.map({ number in number * 2 }) // 通常の書き方
let tripledNumbers = numbers.map { number in number * 3 } // 後置クロージャ
func performAsyncTask(completion: @escaping () -> Void) { print("非同期処理開始...") // ... 時間のかかる処理 ... DispatchQueue.main.asyncAfter(deadline: .now() + 1) { print("非同期処理完了!") completion() // 完了時にクロージャを呼び出す }
}
// 後置クロージャで呼び出し
performAsyncTask { print("完了コールバックが呼ばれました!")
}
値のキャプチャ(特別な構文なし)クロージャは、それが定義されたコンテキスト(周囲のスコープ)にある定数や変数をキャプチャして保持できます。元のスコープが消滅した後でも、それらの値にアクセスしたり変更したりできます。
func makeIncrementer(amount: Int) -> () -> Int { var runningTotal = 0 let incrementer: () -> Int = { // runningTotalとamountをキャプチャしている runningTotal += amount return runningTotal } return incrementer
}
let incrementByTen = makeIncrementer(amount: 10)
print(incrementByTen()) // 10 (runningTotalはキャプチャされ、保持されている)
print(incrementByTen()) // 20
print(incrementByTen()) // 30
let incrementBySeven = makeIncrementer(amount: 7)
print(incrementBySeven()) // 7 (別のrunningTotalをキャプチャ)
エスケープクロージャ (@escaping)func 関数名(completion: @escaping (引数) -> 戻り値型)関数の引数として渡されたクロージャが、関数の実行が終了した後(例: 非同期処理の完了ハンドラなど)に呼び出される可能性があることを示します。明示的に @escaping をつける必要があります。
var completionHandlers: [() -> Void] = []
// クロージャを配列に保存するため、エスケープする必要がある
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) print("クロージャを追加しました。")
}
func someFunctionWithNonescapingClosure(closure: () -> Void) { print("非エスケープクロージャを実行します。") closure() // 関数内で即座に実行される (エスケープしない)
}
someFunctionWithEscapingClosure { print("エスケープクロージャ実行!") }
someFunctionWithNonescapingClosure { print("非エスケープクロージャ実行!") }
print("関数呼び出し後")
// あとで保存したクロージャを実行できる
completionHandlers.first?() // "エスケープクロージャ実行!"
オートクロージャ (@autoclosure)func 関数名(predicate: @autoclosure () -> Bool)引数として渡された式を自動的にクロージャでラップします。これにより、呼び出し側では通常の式のように記述できますが、実際にはクロージャとして渡されます。遅延評価などに使われます。assert 関数などで利用されています。
// assert関数の簡略化例
func myAssert(condition: @autoclosure () -> Bool, message: @autoclosure () -> String) { // condition() は必要な時だけ評価される if !condition() { // message() も必要な時だけ評価される fatalError("Assertion failed: \(message())") }
}
let age = -5
// 呼び出し側では普通の式のように書ける
// age < 0 が false になるまで "Age must be positive" は評価されない
myAssert(condition: age >= 0, message: "Age must be positive") // クラッシュ

構造体とクラス

独自のデータ型を定義するための構成要素です。

機能構造体 (struct)クラス (class)説明
定義struct 名前 { ... }class 名前 { ... }プロパティやメソッドを持つ新しい型を定義します。
インスタンス化let instance = 名前(引数...)let instance = 名前(引数...)定義した型から具体的なオブジェクト(インスタンス)を生成します。イニシャライザ (init) が呼び出されます。
プロパティStored / ComputedStored / Computedインスタンスに関連付けられた値 (Stored) や、計算によって値を返すプロパティ (Computed) を定義できます。
メソッドInstance / TypeInstance / Typeインスタンスや型自体に関連付けられた関数(操作)を定義できます。
イニシャライザinit(引数...) { ... } (デフォルトあり)init(引数...) { ... } (デフォルトなし、必須)インスタンス生成時に初期設定を行うためのメソッドです。クラスは全てのStoredプロパティを初期化するイニシャライザが必須です(デフォルトイニシャライザは条件付きで生成)。構造体はメンバワイズイニシャライザが自動生成されることがあります。
デイニシャライザなしdeinit { ... }クラスのインスタンスがメモリから解放される直前に呼び出される処理です。リソースのクリーンアップなどに使用します。
値型 (Value Type)参照型 (Reference Type)最重要の違い! 構造体のインスタンスは代入や関数渡し時にコピーされます。クラスのインスタンスは代入や関数渡し時に参照(メモリ上のアドレス)がコピーされ、同じインスタンスを指します。
継承不可 可能 (class SubClass: SuperClass)クラスは他のクラスの特性(プロパティ、メソッド)を引き継ぐことができます。構造体は継承できません。
利用推奨場面データのカプセル化、状態が少ない、同等性を値で比較、コピーが望ましい場合(スレッドセーフなど)アイデンティティ(同一性)が重要、状態が多い、Objective-Cとの連携、継承が必要な場合一般的に、シンプルなデータ構造には構造体を、より複雑なオブジェクトや状態管理にはクラスを使用します。Swiftでは構造体が推奨される場面が多いです。

コード例

// --- 構造体 (値型) ---
struct Point { var x: Double var y: Double // メソッド func distance(to other: Point) -> Double { let dx = other.x - self.x let dy = other.y - self.y return sqrt(dx * dx + dy * dy) } // mutatingメソッド (自身のプロパティを変更) mutating func moveBy(x deltaX: Double, y deltaY: Double) { x += deltaX y += deltaY }
}
var p1 = Point(x: 1.0, y: 2.0) // メンバワイズイニシャライザが自動生成
var p2 = p1 // 値がコピーされる
p2.x = 3.0
print(p1.x) // 1.0 (p1は影響を受けない)
print(p2.x) // 3.0
p1.moveBy(x: 0.5, y: 0.5)
print(p1) // Point(x: 1.5, y: 2.5)
// --- クラス (参照型) ---
class Person { var name: String var age: Int weak var bestFriend: Person? // 循環参照を避けるための弱参照 (後述) // イニシャライザ (必須) init(name: String, age: Int) { self.name = name self.age = age print("\(name) が生成されました") } // メソッド func celebrateBirthday() { age += 1 print("Happy Birthday \(name)! You are now \(age).") } // デイニシャライザ (クラスのみ) deinit { print("\(name) が解放されます") }
}
var personA: Person? = Person(name: "Alice", age: 30)
var personB = personA // 参照がコピーされる (同じインスタンスを指す)
personB?.name = "Alicia"
print(personA?.name ?? "nil") // Alicia (personAも変更される)
personA?.celebrateBirthday() // Happy Birthday Alicia! You are now 31.
print(personB?.age ?? 0) // 31
personA = nil // personAの参照を解放
personB = nil // personBの参照も解放 (ここで参照カウントが0になり deinit が呼ばれる)
// 出力: Alicia が解放されます

プロトコルとエクステンション

型の機能や振る舞いを定義し、拡張するための仕組みです。

プロトコル (Protocol)

特定の機能や適合条件を定義するブループリント(設計図)です。クラス、構造体、enum はプロトコルに準拠することで、その機能を持つことを保証します。

目的構文説明
定義protocol プロトコル名 { プロパティ要件; メソッド要件; イニシャライザ要件; ... }準拠する型が実装すべきプロパティ、メソッド、イニシャライザなどを定義します。具体的な実装は含みません。
protocol FullyNamed { var fullName: String { get } // 読み取り専用のコンピューテッドプロパティ要件
}
protocol Runnable { func run() // メソッド要件
}
protocol Initializable { init(name: String) // イニシャライザ要件
}
// 複数のプロトコルを継承
protocol Character: FullyNamed, Runnable { var health: Int { get set } // 読み書き可能なプロパティ要件
}
準拠struct/class/enum 型名: プロトコル1, プロトコル2 { ... }クラス、構造体、またはenumがプロトコルを採用し、定義された要件を全て実装します。クラスがスーパークラスを持つ場合は、スーパークラス名の後にプロトコル名を記述します。
struct Player: Character, Initializable { // CharacterとInitializableに準拠 var firstName: String var lastName: String var health: Int = 100 // Characterの要件 // FullyNamedの要件 (Computed Property) var fullName: String { return "\(firstName) \(lastName)" } // Runnableの要件 func run() { print("\(fullName) is running!") } // Initializableの要件 init(name: String) { let parts = name.split(separator: " ") self.firstName = String(parts.first ?? "") self.lastName = String(parts.last ?? "") } // 独自のイニシャライザも持てる init(firstName: String, lastName: String) { self.firstName = firstName self.lastName = lastName }
}
let player1 = Player(firstName: "Hero", lastName: "Protagonist")
let player2 = Player(name: "Side Kick") // Initializableのinitを使用
print(player1.fullName) // "Hero Protagonist"
player2.run() // "Side Kick is running!"
プロトコル型としての利用var/let 変数名: プロトコル名プロトコル名を型として使用できます。そのプロトコルに準拠する任意の型のインスタンスを代入できます。これにより、具体的な型に依存しない柔軟なコードが書けます(ポリモーフィズム)。
struct Dog: Runnable { func run() { print("Dog is running ") } }
struct Cat: Runnable { func run() { print("Cat is running ") } }
// Runnableプロトコルに準拠するインスタンスを格納できる配列
let animals: [Runnable] = [Dog(), Cat(), Player(name: "NPC")]
// 各要素の具体的な型を知らなくても、run()メソッドを呼び出せる
for animal in animals { animal.run()
}
// Dog is running
// Cat is running
// NPC is running!
委譲 (Delegate) パターンプロトコルを使って実装あるオブジェクト(委譲元)が、自身の処理の一部を別のオブジェクト(デリゲート)に委任するデザインパターンです。プロトコルで委任するメソッドを定義し、デリゲートオブジェクトがそのプロトコルに準拠して実装します。iOS/macOS開発で多用されます。
// 1. 委任する処理をプロトコルで定義
protocol TimerDelegate: AnyObject { // クラス専用プロトコルにするのが一般的 func timerDidTick(currentTime: Int) func timerDidFinish()
}
// 2. 委譲元のクラス
class SimpleTimer { var timer: Timer? var count = 0 let duration: Int weak var delegate: TimerDelegate? // デリゲートへの参照 (循環参照防止のためweak) init(duration: Int) { self.duration = duration } func start() { count = 0 timer?.invalidate() // 既存タイマーを停止 timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in guard let self = self else { return } self.count += 1 // デリゲートに通知 self.delegate?.timerDidTick(currentTime: self.count) if self.count >= self.duration { self.finish() } } } func finish() { timer?.invalidate() timer = nil // デリゲートに通知 delegate?.timerDidFinish() } deinit { print("SimpleTimer解放") }
}
// 3. デリゲートとなるクラス (プロトコルに準拠)
class TimerController: TimerDelegate { var myTimer: SimpleTimer? func setupTimer() { myTimer = SimpleTimer(duration: 5) myTimer?.delegate = self // 自分自身をデリゲートとして設定 myTimer?.start() } // TimerDelegateのメソッドを実装 func timerDidTick(currentTime: Int) { print("Tick: \(currentTime)") } func timerDidFinish() { print("Timer Finished! ") myTimer = nil // タイマーへの参照を解放 } deinit { print("TimerController解放") }
}
let controller = TimerController()
controller.setupTimer()
// 1秒ごとに "Tick: 1", "Tick: 2", ... と出力され、最後に "Timer Finished! " と出力
オプショナル要件 (@objc)@objc protocol プロトコル名 { @objc optional func メソッド要件(...) }Objective-Cとの互換性のために、プロトコルのメソッドやプロパティをオプショナル(実装が任意)にできます。プロトコルと要件の両方に @objc が必要です。Swiftのみで使用する場合は、プロトコルを分割するなどの代替案が推奨されます。
@objc protocol OptionalDataSource { func requiredData() -> String // 必須 @objc optional func optionalSetting() -> Int // 任意
}
class MyDataSource: OptionalDataSource { func requiredData() -> String { return "必須データ" } // optionalSettingは実装しなくても良い
}
class AnotherDataSource: OptionalDataSource { func requiredData() -> String { return "別の必須データ" } func optionalSetting() -> Int { return 123 } // 実装しても良い
}
let source1: OptionalDataSource = MyDataSource()
print(source1.requiredData()) // "必須データ"
// オプショナルメソッドは ? をつけて呼び出す
if let setting = source1.optionalSetting?() { print("設定値: \(setting)")
} else { print("設定はありません") // こちらが出力される
}
let source2: OptionalDataSource = AnotherDataSource()
if let setting = source2.optionalSetting?() { print("設定値: \(setting)") // 設定値: 123
}

エクステンション (Extension)

既存のクラス、構造体、enum、またはプロトコルに新しい機能(メソッド、コンピューテッドプロパティ、イニシャライザなど)を追加します。元のソースコードを変更できない型に対しても機能拡張が可能です。

目的構文説明
機能追加extension 型名 { 新しいメソッド/プロパティ/イニシャライザなど }既存の型に、新しいインスタンスメソッド、タイプメソッド、コンピューテッドプロパティ、イニシャライザを追加します。Storedプロパティや既存の機能の上書きはできません。
// Double型に機能を追加
extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } func squared() -> Double { return self * self }
}
let oneInch = 25.4.mm // 0.0254
print("1インチは \(oneInch) メートルです")
let fiveKm = 5.0.km // 5000.0
print("5kmは \(fiveKm) メートルです")
print(3.0.squared()) // 9.0
// Int型に機能を追加
extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } // イニシャライザの追加 (failable initializer) init?(roman: String) { // 簡単なローマ数字変換 (例) switch roman.uppercased() { case "I": self = 1 case "V": self = 5 case "X": self = 10 default: return nil // 変換できなければnilを返す } }
}
3.repetitions { print("Hello!") // 3回 "Hello!" と出力
}
if let ten = Int(roman: "X") { print(ten) // 10
}
プロトコル準拠の追加extension 型名: プロトコル名 { プロトコルの要件を実装 }既存の型を、後からプロトコルに準拠させることができます。これにより、コードの整理や、外部ライブラリの型への適合などが可能になります。
protocol TextRepresentable { var textualDescription: String { get }
}
struct Dice { let sides: Int var currentRoll: Int init(sides: Int) { self.sides = sides self.currentRoll = Int.random(in: 1...sides) } mutating func roll() { currentRoll = Int.random(in: 1...sides) }
}
// Dice構造体を後からTextRepresentableに準拠させる
extension Dice: TextRepresentable { var textualDescription: String { return "\(sides)面ダイス、現在の目は \(currentRoll)" }
}
var d6 = Dice(sides: 6)
print(d6.textualDescription) // "6面ダイス、現在の目は X" (Xはランダムな値)
d6.roll()
print(d6.textualDescription) // "6面ダイス、現在の目は Y" (Yは新しいランダムな値)
プロトコルへのデフォルト実装extension プロトコル名 { デフォルトのメソッド/プロパティ実装 }プロトコル自体を拡張して、メソッドやコンピューテッドプロパティのデフォルト実装を提供できます。プロトコルに準拠する型は、必要に応じてこのデフォルト実装をそのまま使うか、独自にオーバーライドできます。
protocol Describable { func describe() -> String
}
// Describableプロトコルにデフォルト実装を追加
extension Describable { func describe() -> String { return "これは \(Self.self) 型のインスタンスです。" // Selfは準拠する型自身を指す }
}
struct Product: Describable { var name: String var price: Double // describe() を実装しなくても、デフォルト実装が使われる
}
class Service: Describable { var serviceName: String init(serviceName: String) { self.serviceName = serviceName } // デフォルト実装をオーバーライド func describe() -> String { return "サービス名: \(serviceName)" }
}
let book = Product(name: "Swift Book", price: 3000)
print(book.describe()) // これは Product 型のインスタンスです。
let support = Service(serviceName: "Technical Support")
print(support.describe()) // サービス名: Technical Support

ジェネリクス (Generics)

特定の型に依存しない、柔軟で再利用可能なコードを書くための機能です。型をパラメータとして扱います。

目的構文説明
ジェネリック関数func 関数名<T>(引数: T, ...) -> T { ... }任意の型 T (型パラメータ) を受け入れる関数を定義します。T はプレースホルダーであり、関数呼び出し時に具体的な型が決定されます。複数の型パラメータ (<T, U> など) も可能です。
// 任意の型の値を2つ受け取り、入れ替える関数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt は 107, anotherInt は 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString は "world", anotherString は "hello"
// 複数の型パラメータ
func combine<T, U>(_ first: T, with second: U) -> String { return "Combining \(first) and \(second)"
}
print(combine(10, with: "apples")) // Combining 10 and apples
ジェネリック型struct/class/enum 型名<T> { ... プロパティやメソッドでTを使用 ... }任意の型 T を要素として保持したり、操作したりできる構造体、クラス、またはenumを定義します。Swift標準ライブラリの Array<Element>, Dictionary<Key, Value>, Optional<Wrapped> などが代表例です。
// 任意の型の値を保持できるスタック構造体
struct Stack<Element> { var items: [Element] = [] // Element型の配列 mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element? { return items.popLast() // 配列が空ならnil } func peek() -> Element? { return items.last } var isEmpty: Bool { return items.isEmpty }
}
var intStack = Stack<Int>() // Int型のスタック
intStack.push(1)
intStack.push(2)
print(intStack.pop() ?? 0) // 2
print(intStack.peek() ?? 0) // 1
var stringStack = Stack<String>() // String型のスタック
stringStack.push("apple")
stringStack.push("banana")
print(stringStack.pop() ?? "") // banana
型制約 (Type Constraint)func 関数名<T: プロトコル名>(...)
struct 型名<T: クラス名> { ... }
func 関数名<T>(...) where T: プロトコル1, T: プロトコル2, T == U.AssociatedType { ... }
ジェネリック型パラメータ T が、特定のクラスを継承していることや、特定のプロトコルに準拠していることを要求します。これにより、T が持つべきメソッドやプロパティを保証し、ジェネリックコード内で安全に利用できます。where 句を使うと、より複雑な制約(複数のプロトコル、関連型との比較など)を指定できます。
// Equatableプロトコルに準拠する型 T のみを受け入れる関数
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { // Equatableなので == で比較できる return index } } return nil
}
let doubleIndex = findIndex(of: 9.3, in: [3.14, 0.1, 9.3]) // Optional(2)
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) // Optional(2)
// let nonEquatableIndex = findIndex(of: [1], in: [[1], [2]]) // エラー![Int]はEquatableではない
// 複数の制約とwhere句
protocol Container { associatedtype Item // 関連型 mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get }
}
// 2つのコンテナが同じ型の要素を持ち、その要素がEquatableである場合に、
// 共通の要素があるかチェックする関数
func checkCommonItems<C1: Container, C2: Container> (_ container1: C1, _ container2: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { // where句で制約を追加 for item1 in 0..<container1.count { for item2 in 0..<container2.count { if container1[item1] == container2[item2] { // ItemがEquatableなので比較可能 return true } } } return false
}
// 上記プロトコルに準拠する例 (Stackを使用)
extension Stack: Container { // associatedtype Item は自動的に Element と推論される mutating func append(_ item: Element) { push(item) } var count: Int { return items.count } subscript(i: Int) -> Element { return items[i] }
}
var stack1 = Stack<Int>(); stack1.push(1); stack1.push(2)
var stack2 = Stack<Int>(); stack2.push(2); stack2.push(3)
var stack3 = Stack<String>(); stack3.push("a")
print(checkCommonItems(stack1, stack2)) // true (2が共通)
// print(checkCommonItems(stack1, stack3)) // エラー! Item型が異なる (Int vs String)
プロトコルの関連型 (Associated Type)protocol プロトコル名 { associatedtype 型名 }プロトコルの定義内で、具体的な型を後で(プロトコルに準拠する型によって)決定するためのプレースホルダー名を指定します。ジェネリック型における型パラメータに似ていますが、プロトコル定義内で使用されます。
protocol Container { associatedtype Item // このItemが関連型 var count: Int { get } mutating func append(_ item: Item) subscript(i: Int) -> Item { get }
}
// Intを格納するコンテナ
struct IntContainer: Container { typealias Item = Int // 関連型 Item を Int として具体化 (typealias は省略可能) var items = [Int]() var count: Int { return items.count } mutating func append(_ item: Int) { items.append(item) } subscript(i: Int) -> Int { return items[i] }
}
// Stringを格納するコンテナ
struct StringContainer: Container { // typealias Item = String (省略) var items = [String]() var count: Int { return items.count } mutating func append(_ item: String) { items.append(item) } subscript(i: Int) -> String { return items[i] }
}
// ジェネリックなContainer準拠型
struct GenericContainer<T>: Container { typealias Item = T // 関連型をジェネリックパラメータTで指定 var items = [T]() var count: Int { return items.count } mutating func append(_ item: T) { items.append(item) } subscript(i: Int) -> T { return items[i] }
}

エラーハンドリング

プログラム実行中に発生する可能性のあるエラーに対処するための仕組みです。Swiftでは Error プロトコルと do-catch, try, throw を使ってエラーを表現し、伝播させ、処理します。

目的構文説明
エラー型の定義enum エラー名: Error { case エラーケース1; case エラーケース2(associatedValue: 型) }エラーの種類を表現するためのカスタム型を定義します。通常、Error プロトコルに準拠した enum を使用します。関連値 (Associated Values) を使って、エラーに関する追加情報を持たせることができます。
enum VendingMachineError: Error { case invalidSelection case insufficientFunds(coinsNeeded: Int) case outOfStock
}
enum FileError: Error { case fileNotFound(path: String) case permissionDenied case readFailed(reason: String)
}
エラーのスローthrow エラーインスタンス
func 関数名(...) throws -> 戻り値型
関数やメソッドがエラーを引き起こす可能性があることを示すには、関数の宣言に throws キーワードを追加します。関数内でエラーが発生した場合、throw を使ってエラーを呼び出し元に伝播させます。
struct Item { var price: Int; var count: Int }
var inventory = ["Candy Bar": Item(price: 12, count: 7)]
var coinsDeposited = 10
// エラーをスローする可能性のある関数 (throws)
func vend(itemNamed name: String) throws -> String { guard let item = inventory[name] else { throw VendingMachineError.invalidSelection // 存在しない商品 } guard item.count > 0 else { throw VendingMachineError.outOfStock // 在庫切れ } guard item.price <= coinsDeposited else { // 不足金額を関連値で渡す throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited) } coinsDeposited -= item.price var newItem = item newItem.count -= 1 inventory[name] = newItem return "Dispensing \(name)"
}
// 使用例 (呼び出し側で try が必要)
// let result = try vend(itemNamed: "Chips") // これは do-catch などで囲む必要がある
エラーのキャッチ (do-catch)do { 処理1: let result = try スローする可能性のある関数() ... } catch パターン1 { エラー処理1 } catch パターン2 where 条件 { エラー処理2 } catch { 未知のエラー処理 }throws キーワードを持つ関数を呼び出すには、try キーワードを前置し、その呼び出しを do ブロックで囲みます。catch ブロックで、発生したエラーの型やパターンに応じて処理を分岐します。
func buyFavoriteSnack(person: String, vendingMachine: (String) throws -> String) { let snackName = person == "Alice" ? "Candy Bar" : "Gum" print("\(person) tries to buy \(snackName)") do { let snack = try vendingMachine(snackName) // throws関数をtryで呼び出す print("Success! \(snack)") } catch VendingMachineError.invalidSelection { print("Error: Invalid selection.") } catch VendingMachineError.outOfStock { print("Error: Out of stock.") } catch VendingMachineError.insufficientFunds(let coinsNeeded) { print("Error: Insufficient funds. Please insert an additional \(coinsNeeded) coins.") } catch let error as FileError where error == .permissionDenied { // 型キャストとwhere句 print("File Error: Permission denied.") } catch { // 全ての他のエラーをキャッチ print("An unexpected error occurred: \(error)") // error は自動的に捕捉される } print("---")
}
buyFavoriteSnack(person: "Alice", vendingMachine: vend)
// Alice tries to buy Candy Bar
// Error: Insufficient funds. Please insert an additional 2 coins.
// ---
coinsDeposited = 20
inventory["Gum"] = Item(price: 8, count: 0)
buyFavoriteSnack(person: "Bob", vendingMachine: vend)
// Bob tries to buy Gum
// Error: Out of stock.
// ---
buyFavoriteSnack(person: "Alice", vendingMachine: vend)
// Alice tries to buy Candy Bar
// Success! Dispensing Candy Bar
// ---
エラー変換 (try?)let result = try? スローする可能性のある関数()throws 関数を呼び出し、エラーが発生した場合は結果を nil に、成功した場合は結果をオプショナル型でラップして返します。エラーの詳細情報は失われますが、エラー処理を簡潔に書きたい場合に便利です。
func mightThrow() throws -> Int { // ... 何か処理してエラーをスローするかもしれない ... // return 100 // 成功する場合 throw VendingMachineError.outOfStock // エラーをスローする場合
}
// try? を使う
let successResult = try? mightThrow() // エラーがなければ Optional(100)
print(successResult) // エラーがスローされれば nil
if let value = try? mightThrow() { print("成功しました: \(value)")
} else { print("エラーが発生しましたが、無視します。")
}
エラー強制解決 (try!)let result = try! スローする可能性のある関数()throws 関数を呼び出し、エラーが発生しないことが確実であると想定する場合に使用します。もし実行時にエラーが発生した場合、プログラムはクラッシュします。try! の使用は、エラーが決して起こりえないことが証明できる場合に限定すべきです。
func willNeverThrow() -> Int { return 42 } // throwsしない関数
// throwsしない関数をtry!で呼ぶのは冗長だが可能
let guaranteedResult = try! willNeverThrow()
print(guaranteedResult) // 42
// エラーが起こりえない状況の例 (例: バンドル内の画像読み込み)
// let image = try! loadImage(named: "guaranteed_image.png") // もし画像がなければクラッシュ
// エラーをスローする可能性のある関数で try! を使うと危険
// let crashResult = try! mightThrow() // mightThrowがエラーをスローするとここでクラッシュ!
defer文defer { 実行したい処理 }defer ブロック内のコードは、現在のスコープ(関数、doブロックなど)から退出する直前に必ず実行されます。エラーが発生して途中で退出する場合でも、正常に完了して退出する場合でも実行されます。リソースのクリーンアップ(ファイルハンドルを閉じる、ロックを解除するなど)に非常に便利です。
func processFile(path: String) throws { print("ファイルを開く: \(path)") let fileOpen = true // ダミー // この関数がどのように終了しても (return, throw, 正常終了)、最後に必ず実行される defer { print("ファイルを閉じる: \(path)") // ここで実際のファイルクローズ処理を行う } print("ファイルを読み込み中...") let hasError = Bool.random() // ダミーのエラー発生 if hasError { throw FileError.readFailed(reason: "ランダムエラー発生") } print("ファイル処理完了") // return や関数の終わりでスコープを抜ける
}
do { try processFile(path: "my_data.txt")
} catch { print("エラー発生: \(error)")
}
// 出力例 (エラーなし):
// ファイルを開く: my_data.txt
// ファイルを読み込み中...
// ファイル処理完了
// ファイルを閉じる: my_data.txt
// 出力例 (エラーあり):
// ファイルを開く: my_data.txt
// ファイルを読み込み中...
// ファイルを閉じる: my_data.txt ← エラーでも defer は実行される
// エラー発生: readFailed(reason: "ランダムエラー発生")

非同期処理 (Concurrency)

時間のかかる処理(ネットワーク通信、ファイルI/Oなど)を、メインスレッドをブロックせずに実行するための仕組みです。Swift 5.5以降、async/await, Task, actor といった構造化された並行処理機能が導入されました。

機能構文 / キーワード説明
非同期関数 (async)func 関数名(...) async -> 戻り値型
func 関数名(...) async throws -> 戻り値型
関数が非同期に実行され、処理の途中で中断・再開する可能性があることを示します。throws と同様に、関数のシグネチャに async キーワードを追加します。エラーをスローする可能性のある非同期関数は async throws と記述します。
// ネットワークからデータを非同期に取得する関数の例 (擬似コード)
func fetchData(from url: URL) async throws -> Data { print("Fetching data from \(url)...") // 実際のネットワークリクエスト (中断ポイントになる可能性がある) let (data, response) = try await URLSession.shared.data(from: url) guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw NetworkError.badResponse } print("Data fetched successfully.") return data
}
enum NetworkError: Error { case badResponse, timeout }
非同期関数の呼び出し (await)let result = await 非同期関数(...)
let result = try await スローする非同期関数(...)
async 関数を呼び出す際に、その関数の完了を待つ必要がある場所で await キーワードを使用します。await は中断ポイントとなり、システムは他のタスクを実行できます。非同期関数が throws する場合は try await を使用します。await は別の async 関数や Task の中など、非同期コンテキスト内でのみ使用できます。
// fetchData を呼び出す非同期関数
func downloadAndProcessData() async { guard let url = URL(string: "https://example.com/data.json") else { return } print("Starting download...") do { // fetchDataが完了するまでここで待機 (中断する可能性がある) let data = try await fetchData(from: url) print("Processing \(data.count) bytes...") // ... データ処理 ... print("Processing complete.") } catch { print("Download failed: \(error)") }
}
// この関数を実行するには Task を使う (後述)
// Task {
// await downloadAndProcessData()
// }
タスク (Task)Task { await 非同期処理 }
Task(priority: .background) { ... }
let task = Task { ... }; task.cancel()
非同期処理を実行するための単位です。Task { ... } の形式で、同期的なコードから非同期処理を開始できます。タスクは優先度を指定したり、外部からキャンセルしたりできます。
func performCalculations() async -> Int { print("Heavy calculation started...") try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) // 2秒待機 (非同期) print("Heavy calculation finished.") return 42
}
// 同期コードから非同期処理を開始
print("Before Task")
let calculationTask = Task { // デフォルトは現在のActorの優先度を継承 let result = await performCalculations() print("Calculation result: \(result)") // これは非同期に実行される
}
print("After Task started (but might not be finished)")
// 優先度を指定したタスク
Task(priority: .utility) { await downloadAndProcessData() // 上で定義した関数
}
// タスクのキャンセル (協調的なキャンセルが必要)
Task { let longTask = Task { print("Long task started...") for i in 1...5 { // キャンセルされたか定期的にチェック // try Task.checkCancellation() // キャンセルされていたらエラーをスロー guard !Task.isCancelled else { print("Long task cancelled!") return } print("Long task working... \(i)") try? await Task.sleep(nanoseconds: 1 * 1_000_000_000) } print("Long task finished normally.") } try? await Task.sleep(nanoseconds: 2_500_000_000) // 2.5秒待つ print("Requesting cancellation...") longTask.cancel() // キャンセルをリクエスト
}
構造化された並行性 (Structured Concurrency)async let
TaskGroup
複数の非同期タスクを並行に実行し、それら全ての結果を待つための仕組みです。async let は少数の固定されたタスクを並行実行するのに便利です。TaskGroup は動的な数のタスクを管理するのに適しています。タスクの親子関係が明確になり、エラーハンドリングやキャンセルが容易になります。
func fetchImageData(id: Int) async throws -> Data { let url = URL(string: "https://example.com/image\(id).jpg")! print("Fetching image \(id)") try? await Task.sleep(nanoseconds: UInt64.random(in: 500...1500) * 1_000_000) // ランダムな遅延 print("Fetched image \(id)") return Data("Image\(id)".utf8) // ダミーデータ
}
func downloadMultipleImages() async throws -> [Data] { print("--- Using async let ---") async let image1 = fetchImageData(id: 1) // すぐに実行開始 async let image2 = fetchImageData(id: 2) async let image3 = fetchImageData(id: 3) // ここで await すると、各タスクの結果を待つ // image1, image2, image3 は並行に実行される let results = try await [image1, image2, image3] print("All images downloaded (async let).") return results
}
func downloadImagesWithGroup(ids: [Int]) async throws -> [Int: Data] { print("--- Using TaskGroup ---") var imageData: [Int: Data] = [:] try await withThrowingTaskGroup(of: (Int, Data).self) { group in for id in ids { // グループに子タスクを追加 group.addTask { let data = try await fetchImageData(id: id) return (id, data) // タスクの結果 (IDとデータ) } } // グループ内の全ての子タスクが完了するまで待機 // 完了したものから順次結果を受け取る for try await (id, data) in group { imageData[id] = data print("Received result for image \(id) in group.") } } // グループのスコープを抜けると、全ての子タスクが完了していることが保証される print("All images downloaded (TaskGroup).") return imageData
}
Task { _ = try? await downloadMultipleImages() _ = try? await downloadImagesWithGroup(ids: [4, 5, 6])
}
アクター (Actor)actor アクター名 { ...状態 (プロパティ)... メソッド... }
await アクターインスタンス.メソッド()
nonisolated func メソッド() { ... }
アクターは、内部の状態(プロパティ)へのアクセスを保護し、データ競合を防ぐための参照型です。アクターの外部からそのプロパティやメソッド(nonisolated でないもの)にアクセスするには、常に await が必要です。これにより、一度に一つのタスクだけがアクターの状態を変更できることが保証され、スレッドセーフな状態管理が容易になります。
actor TemperatureSensor { let id: String private var measurements: [Double] = [] private var currentTemp: Double = 20.0 init(id: String) { self.id = id } // アクターの状態を変更するメソッド (外部からは await が必要) func update(temperature: Double) { measurements.append(temperature) currentTemp = temperature print("[\(id)] Updated temp to \(temperature)") } // アクターの状態を読み取るメソッド (外部からは await が必要) func getCurrentTemperature() -> Double { print("[\(id)] Reading temp") return currentTemp } // アクターの状態に依存しないメソッド (nonisolated) // 外部から await なしで呼び出せる nonisolated func getSensorID() -> String { return id } // アクター内部からは await なしで自身のプロパティ/メソッドにアクセス可能 func getAverageTemperature() -> Double { if measurements.isEmpty { return currentTemp } let sum = measurements.reduce(0, +) return sum / Double(measurements.count) }
}
let sensor1 = TemperatureSensor(id: "Living Room")
// 複数のタスクから同時にアクセスしても安全
Task { // アクターのメソッド呼び出しには await が必要 await sensor1.update(temperature: 22.5) let avg = await sensor1.getAverageTemperature() print("Average: \(avg)") // nonisolated なメソッドは await 不要 print("Sensor ID: \(sensor1.getSensorID())")
}
Task { let current = await sensor1.getCurrentTemperature() print("Another task reads temp: \(current)") await sensor1.update(temperature: 21.8)
}
メインアクター (@MainActor)@MainActor class/struct/func/var ...UI更新など、メインスレッドで実行する必要があるコードを指定するためのグローバルアクターです。クラス、構造体、関数、プロパティ全体に @MainActor を付与すると、そのコードは常にメインスレッドで実行されることが保証されます。個別の処理をメインスレッドで実行したい場合は await MainActor.run { ... } を使用します。
// (UIKit/SwiftUIを import している前提の擬似コード)
import SwiftUI // または UIKit
@MainActor // このViewModel全体がメインスレッドで動作
class ContentViewModel: ObservableObject { @Published var statusMessage: String = "Loading..." @Published var fetchedData: String = "" let dataFetcher = DataFetcher() // DataFetcherはメインアクターではないとする func loadData() { // メインアクターから非メインアクターの async 関数を呼ぶ Task { do { // dataFetcher.fetch() は別スレッドで実行される可能性がある let result = try await dataFetcher.fetch() // 結果を受け取った後、UI更新は自動的にメインアクター (メインスレッド) で行われる self.fetchedData = result self.statusMessage = "Data loaded successfully!" } catch { self.statusMessage = "Error loading data: \(error.localizedDescription)" } } } // UIに関係ない処理をバックグラウンドで行い、結果をメインスレッドで反映する例 func performBackgroundWork() { Task(priority: .background) { let result = await performHeavyCalculationInBackground() // 計算結果を使ってUIを更新するためにメインアクターに切り替え await MainActor.run { self.statusMessage = "Background work done: \(result)" } } }
}
// メインアクターではないクラス (例)
class DataFetcher { func fetch() async throws -> String { print("Fetching data on thread: \(Thread.current)") // メインではない可能性 try await Task.sleep(nanoseconds: 1_500_000_000) return "Some fetched data" }
}
func performHeavyCalculationInBackground() async -> Int { print("Calculating on thread: \(Thread.current)") // メインではない可能性 try? await Task.sleep(nanoseconds: 2_000_000_000) return 12345
}
// SwiftUI View (抜粋)
struct ContentView: View { @StateObject var viewModel = ContentViewModel() var body: some View { VStack { Text(viewModel.statusMessage) Text(viewModel.fetchedData) Button("Load Data") { viewModel.loadData() // @MainActor な ViewModel のメソッド呼び出し } Button("Do Background Work") { viewModel.performBackgroundWork() } } .onAppear { // viewModel.loadData() // 画面表示時にロード開始なども } }
}
注意: GCD (Grand Central Dispatch) や OperationQueue も依然として利用可能ですが、新しいコードでは Swift Concurrency (async/await, Task, actor) の利用が推奨されます。これらはより安全で、コードの可読性も向上します。

メモリ管理 (ARC)

SwiftはARC (Automatic Reference Counting) を使用して、クラスインスタンスのメモリを自動的に管理します。インスタンスへの参照がいくつ存在するかを追跡し、参照カウントが0になったときにインスタンスが使用していたメモリを解放します。

通常は意識する必要はありませんが、循環参照 (Retain Cycle) を引き起こさないように注意が必要です。これは、2つ以上のクラスインスタンスが互いを強く参照し合い、参照カウントが0にならずメモリリークが発生する状況です。

参照の種類キーワード説明使用例 / 状況
強参照 (Strong Reference)(デフォルト、キーワードなし)インスタンスへの通常の参照です。参照が存在する限り、インスタンスはメモリ上に保持され、参照カウントを増やします。
class Person { let name: String init(name: String) { self.name = name } deinit { print("\(name) is being deinitialized") }
}
var ref1: Person? = Person(name: "Alice") // ref1は強参照 (参照カウント=1)
var ref2 = ref1 // ref2も強参照 (参照カウント=2)
ref1 = nil // 参照カウント=1
ref2 = nil // 参照カウント=0 -> インスタンス解放
// 出力: Alice is being deinitialized
弱参照 (Weak Reference)weak var 変数名: 型?インスタンスへの参照を持ちますが、参照カウントを増やしません。参照先のインスタンスが解放されると、自動的に nil になります。そのため、弱参照は常にオプショナル型 (?) の変数 (var) として宣言する必要があります。循環参照を解決する一般的な方法です。
class Person { let name: String var apartment: Apartment? // Person -> Apartment (強参照) init(name: String) { self.name = name } deinit { print("Person \(name) deinitialized") }
}
class Apartment { let unit: String // Apartment -> Person (弱参照: weak) // ここを弱参照にしないと循環参照になる weak var tenant: Person? init(unit: String) { self.unit = unit } deinit { print("Apartment \(unit) deinitialized") }
}
var john: Person? = Person(name: "John") // john参照カウント=1
var unit4A: Apartment? = Apartment(unit: "4A") // unit4A参照カウント=1
// 相互に参照を設定
john?.apartment = unit4A // unit4A参照カウント=2 (Person -> Apartment は強参照)
unit4A?.tenant = john // john参照カウントは増えない (Apartment -> Person は弱参照)
// 参照を解放
john = nil // johnの強参照が解放される -> john参照カウント=0 -> Person解放
// Personが解放されると、それに紐づくapartmentへの強参照もなくなる
// -> unit4A参照カウント=1 (元のunit4A変数からの参照のみ)
print("John released")
unit4A = nil // unit4Aの強参照が解放される -> unit4A参照カウント=0 -> Apartment解放
print("Unit4A released")
// 出力順序は不定だが、両方解放される:
// Person John deinitialized
// John released
// Apartment 4A deinitialized
// Unit4A released
状況: 一方のインスタンスがもう一方を所有するが、所有される側も所有者を参照する必要がある場合(例: Delegateパターン、親子関係)。ライフサイクルが独立している可能性がある場合。
非所有参照 (Unowned Reference)unowned var/let 変数名: 型弱参照と同様に参照カウントを増やしませんが、参照先のインスタンスが解放された後も nil になりません。解放されたインスタンスにアクセスしようとするとクラッシュします。参照先のインスタンスが、非所有参照を持つインスタンスよりも常に長く生存することが保証されている場合に使用します。そのため、通常はオプショナル型ではなく、定数 (let) で宣言されることも多いです。
class Customer { let name: String // Customer -> Card (強参照, Customerが存在する限りCardも存在する想定) var card: CreditCard? init(name: String) { self.name = name } deinit { print("Customer \(name) deinitialized") }
}
class CreditCard { let number: UInt64 // Card -> Customer (非所有参照: unowned) // CardはCustomerなしには存在しえないと仮定 unowned let customer: Customer init(number: UInt64, customer: Customer) { self.number = number self.customer = customer } deinit { print("Card #\(number) deinitialized") }
}
var bob: Customer? = Customer(name: "Bob") // bob参照カウント=1
// bobが存在する状態でCardを作成し、相互参照を設定
bob?.card = CreditCard(number: 1234_5678_9012_3456, customer: bob!)
// bob?.card は Cardへの強参照 (Card参照カウント=1)
// Cardインスタンス内の customer は bobへの非所有参照 (bob参照カウントは増えない)
// Customerを先に解放
bob = nil // bob参照カウント=0 -> Customer解放
// Customerが解放されると、それに紐づくcardへの強参照もなくなる
// -> Card参照カウント=0 -> CreditCard解放
// 出力:
// Customer Bob deinitialized
// Card #1234567890123456 deinitialized
// もしCardがCustomerより長く生存しようとすると問題が発生する
// (この例では発生しないが、設計に注意が必要)
状況: 2つのインスタンスが常に同時に破棄される、または一方が他方なしには存在しえず、他方が常に先に破棄されないことが確実な場合。ライフサイクルが密接に関連している場合。
クロージャ内のキャプチャリスト{ [weak self, unowned other] (引数) -> 戻り値型 in ... }クロージャがクラスインスタンスのメソッドやプロパティを参照する場合、暗黙的にそのインスタンスへの強参照をキャプチャし、循環参照を引き起こす可能性があります(特にクロージャがインスタンスのプロパティとして保持される場合)。キャプチャリスト [weak self][unowned self] を使用して、self (または他のインスタンス) への参照を弱参照または非所有参照としてキャプチャすることで、循環参照を防ぎます。weak self の場合、クロージャ内で self はオプショナル型になるため、guard let self = self else { return } などでアンラップして使用します。
class HTMLElement { let name: String let text: String? // このクロージャはselfをキャプチャする可能性がある // lazy var は初期アクセス時に一度だけ評価されるプロパティ lazy var asHTML: () -> String = { [weak self] in // selfを弱参照でキャプチャ guard let self = self else { // selfが解放されていたらデフォルト値を返す return "" } if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text print("HTMLElement \(name) initialized") } deinit { print("HTMLElement \(name) deinitialized") }
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "Hello, world")
print(paragraph!.asHTML()) // 

Hello, world

// paragraphへの参照をなくす paragraph = nil // HTMLElement とクロージャ間の循環参照がないため、インスタンスは解放される // 出力: HTMLElement p deinitialized // --- 循環参照の例 (キャプチャリストなし) --- class BadHTMLElement { let name: String lazy var describe: () -> Void = { // ここで self を強参照でキャプチャしてしまう print("This is a \(self.name) element.") } init(name: String) { self.name = name } deinit { print("BadHTMLElement \(name) deinitialized") } // 解放されない } var badElement: BadHTMLElement? = BadHTMLElement(name: "div") badElement?.describe() // クロージャを評価してselfをキャプチャさせる badElement = nil // 解放しようとしても、クロージャが強参照を持っているので解放されない! // deinitメッセージは出力されない (メモリリーク)

ARCの基本ルール

  • クラスインスタンスを作成すると、参照カウントは1になります。
  • 変数がインスタンスへの参照を保持すると、参照カウントが+1されます(強参照の場合)。
  • 変数が参照を解放する(nilを代入、スコープを抜けるなど)と、参照カウントが-1されます。
  • 参照カウントが0になると、インスタンスはメモリから解放され、deinitが呼ばれます。
  • 循環参照を防ぐために、weak または unowned を適切に使用しましょう。迷ったら weak が安全です。

その他

その他の重要なSwiftの機能です。

機能キーワード / 構文説明
アクセスコントロールopen
public
internal
fileprivate
private
コード(クラス、構造体、プロパティ、メソッドなど)へのアクセスレベルを制限します。
  • open: 最も制限が緩い。モジュール外から継承・オーバーライド可能(クラスとクラスメンバのみ)。
  • public: モジュール外からアクセス可能だが、継承・オーバーライドはモジュール内のみ。
  • internal: (デフォルト) 同じモジュール内からのみアクセス可能。
  • fileprivate: 同じソースファイル内からのみアクセス可能。
  • private: それを囲む宣言(クラス、構造体など)のスコープ内、および同じファイル内のその宣言のエクステンションからのみアクセス可能。最も制限が強い。
// --- MyFramework.framework ---
open class OpenClass { // モジュール外で継承・オーバーライド可能 open var openProperty: Int = 0 public var publicProperty: Int = 0 // モジュール外からアクセス可能 internal var internalProperty: Int = 0 // モジュール内からのみ fileprivate var fileprivateProperty: Int = 0 // このファイル内からのみ private var privateProperty: Int = 0 // このクラス内(+同一ファイル拡張)からのみ open func openMethod() {} public func publicMethod() {} internal func internalMethod() {} fileprivate func fileprivateMethod() {} private func privateMethod() {}
}
public class PublicClass { // モジュール外でアクセス可能だが継承不可 // ...
}
// --- App (別のモジュール) ---
// import MyFramework
// let instance = OpenClass() // OK
// instance.openProperty = 1 // OK
// instance.publicProperty = 1 // OK
// instance.internalProperty = 1 // Error! internalはアクセス不可
// instance.fileprivateProperty = 1 // Error!
// instance.privateProperty = 1 // Error!
// class SubOpenClass: OpenClass { // OK (openなので継承可能)
// override func openMethod() {} // OK (openなのでオーバーライド可能)
// override func publicMethod() {} // Error! publicはモジュール外でオーバーライド不可
// }
// class SubPublicClass: PublicClass {} // Error! publicはモジュール外で継承不可
型キャストis (型チェック)
as? (ダウンキャスト – オプショナル)
as! (ダウンキャスト – 強制)
as (アップキャスト / ブリッジング)
インスタンスの型を確認したり、スーパークラスやプロトコルの型から、より具体的なサブクラスや準拠型へ変換(ダウンキャスト)したりします。
  • is: インスタンスが特定の型かどうかを Bool でチェックします。
  • as?: 安全なダウンキャスト。成功すれば指定した型のオプショナル値を返し、失敗すれば nil を返します。
  • as!: 強制ダウンキャスト。キャストが失敗すると実行時エラーでクラッシュします。成功が確実な場合にのみ使用します。
  • as: アップキャスト(サブクラスからスーパークラスへ)や、保証されたブリッジング(Swift型とObjective-C型間など)に使用します。ダウンキャストには使用できません。
class MediaItem { var name: String; init(name: String) { self.name = name } }
class Movie: MediaItem { var director: String; init(name: String, director: String) { self.director = director; super.init(name: name) } }
class Song: MediaItem { var artist: String; init(name: String, artist: String) { self.artist = artist; super.init(name: name) } }
let library: [MediaItem] = [ Movie(name: "Casablanca", director: "Michael Curtiz"), Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), Movie(name: "Citizen Kane", director: "Orson Welles")
]
var movieCount = 0
var songCount = 0
for item in library { // is: 型チェック if item is Movie { movieCount += 1 // as!: 強制ダウンキャスト (ここでは is で確認済みなので安全) let movie = item as! Movie print("Movie: \(movie.name), dir. \(movie.director)") } else if item is Song { songCount += 1 // as?: 安全なダウンキャスト if let song = item as? Song { print("Song: \(song.name), by \(song.artist)") } }
}
print("\(movieCount) movies and \(songCount) songs.")
// アップキャスト (as)
let specificMovie = Movie(name: "Psycho", director: "Hitchcock")
let generalMedia: MediaItem = specificMovie // 暗黙的アップキャスト、または as MediaItem と明記可能
パターンマッチング(switch, if case, guard case, for case)値が特定のパターンに一致するかどうかを調べ、一致した場合に関連する値を抽出(バインド)する強力な機能です。switch 文だけでなく、if, guard, for ループでも限定的なパターンマッチングが可能です。
enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String)
}
var productBarcode: Barcode = .upc(8, 85909, 51226, 3)
// productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
// switch でのパターンマッチング
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check): print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode): print("QR code: \(productCode)")
}
// if case でのパターンマッチング
if case let .qrCode(code) = productBarcode { print("This is a QR Code (using if case): \(code)")
} else { print("This is not a QR Code (using if case)")
}
let codes: [Barcode] = [.upc(1, 2, 3, 4), .qrCode("XYZ"), .upc(5, 6, 7, 8)]
// for case で特定のケースのみを処理
print("--- Processing only QR Codes (for case) ---")
for case let .qrCode(code) in codes { print("Found QR Code: \(code)")
}
// guard case (オプショナルとの組み合わせなど)
let optionalCode: Barcode? = .qrCode("12345")
func processQRCode(code: Barcode?) { guard case let .qrCode(value)? = code else { print("Not a QR Code or nil") return } print("Processing QR Code (guard case): \(value)")
}
processQRCode(code: optionalCode)
processQRCode(code: .upc(0, 0, 0, 0))
processQRCode(code: nil)
Any, AnyObjectAny
AnyObject
特定の型が不明な場合や、様々な型の値を扱いたい場合に使用できる特殊な型です。
  • Any: 関数型やオプショナル型を含む、あらゆる型のインスタンスを表すことができます。
  • AnyObject: あらゆるクラス型のインスタンスを表すことができます(プロトコルで : AnyObject を指定するとクラス専用プロトコルになる)。
これらの型を使用する場合、実際に利用する際には型キャスト (as?, as!) が必要になることが多く、型安全性が低下するため、可能な限り具体的な型を使用することが推奨されます。
var things: [Any] = []
things.append(0)
things.append(0.0)
things.append(42)
things.append("hello")
things.append((3.0, 5.0)) // タプルもOK
things.append({ (name: String) -> String in "Hello, \(name)" }) // クロージャもOK
things.append(Movie(name: "Finding Nemo", director: "Andrew Stanton")) // クラスインスタンスもOK
for thing in things { switch thing { case 0 as Int: print("zero as an Int") case 0 as Double: print("zero as a Double") case let someInt as Int: print("an integer value of \(someInt)") case let someDouble as Double where someDouble > 0: print("a positive double value of \(someDouble)") case let someString as String: print("a string value of \"\(someString)\"") case let (x, y) as (Double, Double): print("an (x, y) point at \(x), \(y)") case let stringConverter as (String) -> String: print(stringConverter("Michael")) case let movie as Movie: print("a movie called \(movie.name), dir. \(movie.director)") default: print("something else: \(type(of: thing))") }
}
// AnyObject の例
let objects: [AnyObject] = [ Movie(name: "Up", director: "Pete Docter"), NSString("bridged string"), // Objective-Cのクラス
]
// objects.append("Swift String") // エラー! Stringはクラスではない
// objects.append(10) // エラー! Intはクラスではない

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です