戌咬音かんぬき(inugaminé)'s avatar

戌咬音かんぬき(inugaminé)

変数と定数

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)データと機能をまとめた設計図。参照型。
インスタンス設計図から作られた実際のモノ
プロパティ構造体/クラスが持つ変数・定数
メソッド構造体/クラスが持つ関数
値型代入・渡すときにコピーされる型
参照型代入・渡すときに参照 (アドレス) が共有される型
構造体とクラスは「データと機能をまとめた設計図」

IntStringArrayDictionary… これらはすべて構造体として実装されている。

なぜ必要なのか?

例えば「画面の解像度」を扱いたいとする。

// バラバラに管理すると...
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)も使える。

→ 詳細は プロパティ を参照