変数と定数
Swift では let で定数、var で変数を作成する。
迷ったら let を使うのが基本ルール。安全性が高くバグが減る。
let userName = "Ken" // 定数: 変更不可
var score = 0 // 変数: 変更可能
→ 詳細は 変数と定数 を参照
イニシャライザ (Initializer)
イニシャライザとは、値を作る仕組みのことである。
var number = Int() // Int() がイニシャライザ(初期化関数)
その他の例
let emptyArray = Array<Int>() // 空の配列を作る
let emptySet = Set<String>() // 空のセットを作る
let emptyDict = Dictionary<Int, String>() // 空の辞書を作る
// 簡略版 -セットの簡略版は無い
let emptyArray = [Int]()
let emptyDict = [Int: String]() // または [:]
型
Swift は型安全な言語であり、間違った型を使おうとするとコンパイル時にエラーになる。
また、型推論により型を明示しなくても Swift が自動で判断してくれる。
let age = 30 // Int と推論
let name = "Ken" // String と推論
let age: Int = 30 // 型注釈で明示も可能
→ 詳細は 型 を参照
リテラル (Literal)
リテラルとはコードに直接書かれた値のこと。
let number = 42 // 数値リテラル
let text = "Hello" // 文字列リテラル
let list = [1, 2, 3] // 配列リテラル
let dict = ["key": "value"] // 辞書リテラル
つまり: let text = "Hello の場合
- 左辺 (
let text) → [[型]]の宣言 - 右辺 (
"Hello") → 文字列リテラル (実際の値)
値
データそのもの。
var score = 100
// ↑変数 ↑値
let age = 30
// ↑定数 ↑値
score = 150 // 箱の中身を変更 (変数だからOK)
age = 31 // エラー! (定数は変更できない)
コレクション型
Swift は 3 つの基本的なコレクション型を提供している:
| 型 | 特徴 |
|---|---|
| 配列 (Array) | 順序付きリスト、重複OK |
| セット (Set) | 順序なし、重複不可 |
| 辞書 (Dictionary) | キーと値のペア |
var fruits = ["りんご", "バナナ"] // 配列
var colors: Set = ["赤", "緑", "青"] // セット
var airports = ["HND": "羽田", "NRT": "成田"] // 辞書
要素へのアクセスは [] を使う (サブスクリプト構文):
fruits[0] // "りんご" (インデックスでアクセス)
airports["HND"] // Optional("羽田") (キーでアクセス)
→ 詳細は コレクション型 を参照
制御フロー
Swift は様々な制御フロー文を提供している。ループ、条件分岐、制御転送文を使ってコードを構造化する。
| カテゴリ | 文 | 用途 |
|---|---|---|
| ループ | for-in, while, repeat-while | 繰り返し処理 |
| 条件分岐 | if, switch, guard | 条件による分岐 |
| 制御転送 | continue, break, fallthrough, return, throw | 実行フローの制御 |
| その他 | defer, #available | 遅延実行、API チェック |
// for-in
for i in 1...5 {
print(i)
}
// if
if temperature > 30 {
print("暑い")
}
// switch
switch value {
case 1:
print("one")
default:
print("other")
}
// guard
guard let name = person["name"] else { return }
print(name)
→ 詳細は 制御フロー を参照
関数
関数は特定のタスクを実行する独立したコードの塊。名前で呼び出すことができる。
func greet(person: String) -> String {
return "こんにちは、" + person + "さん"
}
print(greet(person: "Anna")) // こんにちは、Annaさん
| 用語 | 役割 |
|---|---|
| 引数 | 関数に渡す入力値 |
| 戻り値 | 関数から帰ってくる出力値 |
| return | 値を返す / 関数を終了する |
→ 詳細は 関数 を参照
クロージャ (Closures)
名前付き[[関数]]を作成せずに一緒に実行するコードをグループ化する。
[[クロージャ]]は、コード内で受け渡して使用できる、ある機能の独立したブロックである。
実は関数もクロージャの一種である。
| 形式 | 名前 | キャプチャ |
|---|---|---|
| グローバル関数 | あり | しない |
| ネスト関数 | あり | 外側の関数から可能 |
| クロージャ式 | なし | 周囲のコンテキストから可能 |
| クロージャ式とは「名前がなく、周りの値をキャプチャできる軽量な関数」をいう。 |
Swift のクロージャの特徴 (省略できるポイント)
Swift のクロージャは、以下の省略ができるように最適化されている。
- 型を推論できる
- 単一式なら
returnを省略できる - 引数名を
$0,$1で省略できる - 末尾クロージャ構文が使える
自動クロージャ(@autoclosure)を使うことで、呼び出す際にクロージャ特有の中括弧を省略することができる。
また、自動クロージャの本当の価値は 遅延評価 にある。
→ 詳細は クロージャ を参照
列挙型 (Enumerations)
列挙型とは、「あらかじめ決まった選択肢の中から 1 つを選ぶ」ための型のこと。
方角なら下記の 4 つ。曜日なら 7 つ。
enum CompassPoint {
case north
case south
case east
case west
}
var direction = CompassPoint.north
このような「取りうる値が限られている」ものを表現するのに最適である。
Swift の列挙型が強力な理由:
| 機能 | 説明 |
|---|---|
| それ自体が独立した型 | 整数のエイリアスではなく、(この例では) CompassPoint という型そのもの |
| 関連値 (Associated Value) | ケースごとに「追加データ」を持てる |
| Raw Values | 全ケースに共通の型で「生の値」を持てる |
| メソッド・プロパティが持てる | クラスや構造体のように振る舞うことができる |
| プロトコル準拠 | CaseIterable で全ケースをループしたりできる |
関連値 vs Raw Values
データを持たせる方法が 2 種類ある。
Raw Value (生の値)
- 定義時に決まる固定値
- 全ケースが同じ型
- 例: 曜日に 1〜7 の番号を振る
enum Weekday: Int {
case sunday = 1, monday, tuesday // 1, 2, 3...
}
関連値 (Associated Values)
- インスタンス生成時に決まる可変値
- ケースごとに型が違っていてもOK
- 例: バーコードの種類によって持つデータが違う
enum Barcode {
case upc(Int, Int, Int, Int) // 4つの整数
case qrCode(String) // 文字列
}
→ 詳細は 列挙型 を参照
構造体とクラス (Structures and Classes)
| 用語 | 意味 |
|---|---|
| 構造体 (struct) | データと機能をまとめた設計図。値型。 |
| クラス (class) | データと機能をまとめた設計図。参照型。 |
| インスタンス | 設計図から作られた実際のモノ |
| プロパティ | 構造体/クラスが持つ変数・定数 |
| メソッド | 構造体/クラスが持つ関数 |
| 値型 | 代入・渡すときにコピーされる型 |
| 参照型 | 代入・渡すときに参照 (アドレス) が共有される型 |
構造体とクラスは「データと機能をまとめた設計図」
Int、String、Array、Dictionary… これらはすべて構造体として実装されている。
なぜ必要なのか?
例えば「画面の解像度」を扱いたいとする。
// バラバラに管理すると...
var width1 = 1920
var height1 = 1080
var width2 = 1280
var height2 = 720
//どれがどのペアか分かりにくい。
// 構造体でまとめると...
struct Resolution {
var width: Int
var height: Int
}
let fullHD = Resolution(width: 1920, heigh: 1080)
let hd = Resolution(width: 1280, heigh: 720)
分かりやすい。
関連するデータをひとまとめにして、扱いやすくする。これが構造体・クラスの基本的な目的である。
構造体 vs クラス - 最大の違い
┌──────────────────────────────────────────────────┐
│ 共通点 │
│ ・プロパティ(データ)を持てる │
│ ・メソッド(機能)を持てる │
│ ・イニシャライザで初期化できる │
│ ・extensionで拡張できる │
│ ・プロトコルに準拠できる │
└──────────────────────────────────────────────────┘
┌─────────────────────┐ ┌────────────────────┐
│ 構造体 (struct) │ │ クラス (class) │
├─────────────────────┤ ├────────────────────┤
│ ★ 値型 │ │ ★ 参照型 │
│ (コピーされる) │ │ (参照が共有される)│
├─────────────────────┤ ├────────────────────┤
│ メンバワイズ │ │ 継承できる │
│ イニシャライザ自動生成 │ │ デイニシャライザあり │
├─────────────────────┤ ├────────────────────┤
│ シンプル・安全 │ │ 柔軟・複雑 │
│ → 推奨! │ │ → 必要な時だけ使う │
└─────────────────────┘ └────────────────────┘
値型 vs 参照型
値型 (構造体) の場合
var a = Resolution(width: 1920, height: 1080)
var b = a // コピーが作られる
b.width = 1280
// メモリのイメージ
a b
┌──────────────┐ ┌──────────────┐
│ width: 1920 │ │ width: 1280 │ ← b を width:1280 に変えても
│ height: 1080 │ │ height: 1080 │ a の width:1920 は変わらない
└──────────────┘ └──────────────┘
別々の実体 別々の実体
参照型 (クラス) の場合
var a = VideoMode()
a.frameRate = 25.0
var b = a // 参照がコピーされる (実体は同じ)
b.frameRate = 30.0
メモリのイメージ
a b
│ │
│ │
▼ ▼
┌───────────────┐
│frameRate: 30.0│ ← b を変えると a も変わる
└───────────────┘ (同じ実体を指してるから)
1つの実体
Swift での使い分け方針
基本は構造体を使う。
Apple の公式ガイドライン:
「構造体のほうが扱いやすく推奨される。クラスは適切または必要な場合にのみ使用してください。」
- デフォルト → 構造体
- 継承が必要、または参照共有が必要 → クラス
→ 詳細は 構造体とクラス を参照
プロパティ
プロパティとは、値を特定のクラス・構造体・列挙型に関連付ける仕組みのこと。 大きく分けると3種類ある。
①格納プロパティ (Stored Properties)
値をインスタンスの中に直接保存する。一番シンプルなもの。
struct Dog {
var name: String // 変数格納プロパティ
let breed: String // 定数格納プロパティ
}
格納プロパティを使えるのはクラスと構造体のみ。列挙型には使えない。
②計算プロパティ (Computed Properties)
値を保存せず、アクセスされるたびに計算して返す。
struct Circle {
var radius: Double
var area: Double { // 計算プロパティ
return radius * radius * .pi
}
}
これはクラス・構造体・列挙型で使える。
③型プロパティ (Type Properties)
上記2つは「インスタンスごと」に持つプロパティだったが、型そのものに紐づくプロパティもある。これは static で定義する。
struct Config {
static let maxRetries = 3 // 型プロパティ
}
// Config.maxRetries でアクセス (インスタンス不要)
さらに追加機能として:
- プロパティオブザーバ - 値の変更を監視する (
willSet/didSet) - プロパティラッパー - getter / setter のロジックを再利用可能にまとめる
全体の関係のまとめ
プロパティ
├── インスタンスプロパティ (インスタンスごとに持つ)
│ ├── 格納プロパティ ── クラス・構造体のみ
│ └── 計算プロパティ ── クラス・構造体・列挙型
│
├── 型プロパティ (型そのものに紐づく)
│ ├── 格納型プロパティ
│ └── 計算型プロパティ
│
├── [追加機能] プロパティオブザーバ (willSet / didSet)
└── [追加機能] プロパティラッパー (@propertyWrapper)
ポイントは、列挙型には格納プロパティがないというところ。
列挙型は各ケースが「値そのもの」を表す型なので、インスタンスに値を保存する仕組みとは相性が違う。
ただし、計算プロパティは定義でき、型プロパティ(static)も使える。
→ 詳細は プロパティ を参照