Swift

構造体・クラス・プロトコル

コピーと参照の違い、mutating、アクセス制御、プロトコルを小さな例で確認します。

対象:Swift 6系。基礎はコンソールで学びます。iOSアプリの開発にはmacOSとXcodeが必要です。

01

学ぶことと準備

入門の構造体と関数を理解したら、データの変更がどこへ影響するかを学びましょう。例はそれぞれ独立しています。main.swiftの全体を置き換えて swift main.swift で実行します。

Windowsでは 入門のコンパイル手順と同じく、main.swiftを書き換えるたびにコンパイルして実行します。

02

構造体は値としてコピーする

構造体を別の変数に代入すると値がコピーされます。プロパティを変更するメソッドには mutating が必要です。変更するインスタンスはvarで持ちます。

main.swift(全体を置き換え)SWIFT
struct Counter {
    private(set) var count = 0
    mutating func increment() { count += 1 }
}
var first = Counter()
var second = first
second.increment()
print(first.count)
print(second.count)
実行結果OUTPUT
0
1

private(set) は外から読み取れる一方、直接の書き換えは型の内部に制限します。first.count = 10 はこの型の外ではコンパイルエラーです。

03

クラスは同じ実体を参照する

クラスのインスタンスを代入すると、同じ実体への参照を共有します。letは参照の差し替えを禁止しますが、参照先のvarプロパティの変更までは禁止しません。

main.swift(全体を置き換え)SWIFT
final class Counter {
    private(set) var count = 0
    func increment() { count += 1 }
}
let first = Counter()
let second = first
second.increment()
print(first.count)
print(second.count)
print(first === second)
実行結果OUTPUT
1
1
true

=== は同じインスタンスかを調べます。final は継承を禁止する指定です。同じ実体を共有する必要があるかを基準に構造体とクラスを選びます。

04

initで初期値を決める

クラスでは init を定義して、作成時に必要な値を受け取ります。self は現在のインスタンスを指します。引数とプロパティが同名のときに区別できます。

main.swift(全体を置き換え)SWIFT
final class Product {
    let name: String
    let price: Int
    init(name: String, price: Int) {
        self.name = name
        self.price = price
    }
    func label() -> String { return "\(name): \(price)円" }
}
let pen = Product(name: "ペン", price: 120)
print(pen.label())
実行結果OUTPUT
ペン: 120円
05

プロトコルと拡張

プロトコルは型に求める機能を定義します。以下のNamedはnameを読めることを要求します。extension で共通のメソッドを追加すると、適合した型が使えます。

main.swift(全体を置き換え)SWIFT
protocol Named { var name: String { get } }
extension Named {
    func greeting() -> String { return "こんにちは、\(name)さん" }
}
struct Member: Named { let name: String }
let member = Member(name: "Mity")
print(member.greeting())
実行結果OUTPUT
こんにちは、Mityさん
06

練習:在庫を守る

Stock構造体を作り、在庫が1以上のときだけ1個販売できるsellメソッドを追加します。在庫1から2回販売し、true、false、0と表示してください。

解答例と結果を確認する

まず自分で書いてから、処理の順番と結果を比べましょう。

main.swift(全体を置き換え)SWIFT
struct Stock {
    private(set) var count: Int
    init(count: Int) { self.count = max(0, count) }
    mutating func sell() -> Bool {
        guard count > 0 else { return false }
        count -= 1
        return true
    }
}
var stock = Stock(count: 1)
print(stock.sell())
print(stock.sell())
print(stock.count)
実行結果OUTPUT
true
false
0