値を表すモデルではstructから検討し、同一オブジェクトの識別性や状態共有が必要ならclassを選びます。オブジェクトをstructで包んでも、独立性やスレッド安全性は自動で得られません。
仕組みと設計の判断基準
構造体の代入は格納された値をコピーします。プロパティがクラス参照なら、コピーされるのは参照であり、参照先は共有されます。深いコピーやスレッド安全性とは区別してください。継承やObjective-CのオブジェクトAPIも選択に関係します。 一次資料
再現コード
Pointの整数、Boxの参照、Boxを持つEnvelopeを順にコピーします。オブジェクトのプロパティ変更と、コピー側のboxを新しいオブジェクトへ置き換える操作を分けました。各段階で原本とコピーの値をpreconditionで確認します。
この独立したサンプルは2026-10-23に、arm64 macOS向けのApple Swift 6.4をSwift 6言語モードで実行しました。ネットワーク接続や追加パッケージは不要です。
ダウンロード先のフォルダーで実行してください。検証条件を満たさない場合はpreconditionで停止します。検証が削除されるため、-Ouncheckedは使用しないでください。
swift -swift-version 6 swift-class-vs-struct.swift
struct Point { var x: Int }
final class Box {
var x: Int
init(_ x: Int) { self.x = x }
}
struct Envelope { var box: Box }
let point = Point(x: 0)
var copiedPoint = point
copiedPoint.x = 10
precondition(point.x == 0 && copiedPoint.x == 10)
let box = Box(0)
let copiedReference = box
copiedReference.x = 10
precondition(box === copiedReference && box.x == 10)
let envelope = Envelope(box: Box(0))
var copiedEnvelope = envelope
copiedEnvelope.box.x = 20
precondition(envelope.box.x == 20)
copiedEnvelope.box = Box(30)
precondition(envelope.box.x == 20 && copiedEnvelope.box.x == 30)
print("struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30")
実行結果と読み取り方
struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30
Pointは0と10に分かれ、Boxへの参照は10を共有します。コピーしたEnvelopeからオブジェクトを変更すると両方が20になります。一方、コピー側のboxをBox(30)へ置き換えても原本は20のままです。
適用範囲と注意点
この逐次実行の実験はコピーの意味を確認するもので、競合や性能は測定していません。変更可能な変数や内部参照を共有するなら、隔離や同期の検討が必要です。Swift 6のSendable検査も、struct宣言だけと同義ではありません。
次に確認すること
モデルをコピーした後も一緒に変わるべき状態を列挙してください。意図しない参照共有には、不変の値や明示的な複製方針を検討します。大きな保存領域を効率よくコピーする話はCoWの実験に続きます。
出典と確認基準
- The Swift Programming Language: Structures and ClassesSwift.org · 公式ドキュメント · 確認日 2026年10月23日根拠: 実装の契約と説明の根拠です。出力は、この記事に掲載した実験で別途確認しました。

