Objective-CやJavaを先に学んでからSwiftに移ると、必ず一度は引っかかるポイントがあります。
それは「オブジェクトを複製するにはどうすればいいのか?」という疑問です。
clone()のようなメソッドを探し回りがちですが、Swiftではその必要はほとんどありません。
結論からお話しします。
Swiftは、構造体や配列などの値型とCopy-on-Write(コピーを遅延させる最適化)によって、デザインパターンでいう「プロトタイプパターン」を言語レベルで置き換えます。
オブジェクトを複製するために別のパターンを設計する必要はなく、値を代入するだけで安全なコピーが得られます。
この記事では、プロトタイプパターンが本来何を解決しようとしたのか、Swiftの値型とCopy-on-Writeがそれをどのように自然に取り込むのかを、例とともに整理します。
コピーの種類を区別するには、まず浅いコピー vs 深いコピーが基礎になります。クラスの複製まで必要な場合は、SwiftのプロトタイプパターンとNSCopyingがもう一つの選択肢を示します。
プロトタイプパターンが本来解決しようとした問題
プロトタイプパターンはGoFデザインパターンの生成パターンに属します。
要点は簡単です。新しいオブジェクトを最初から作る代わりに、既存のオブジェクトを複製して新しいインスタンスを得ます。
なぜこのような仕組みが必要だったのでしょうか?
Objective-CやJavaなどの言語では、オブジェクトの多くが参照型です。
オブジェクトを変数に代入すると、値ではなく同じオブジェクトを指すアドレスだけがコピーされます。
そのため、Aを変更するとBも一緒に変わってしまいます。
これを避けるため、開発者はclone()やcopy()メソッドを自分で作り、「本当のコピー」を返すようにしました。
プロトタイプパターンは、こうした状況のための道具です。
- オブジェクトの生成コストが高く、似たオブジェクトが複数必要なとき
- 元のオブジェクトに触れず、独立したコピーが必要なとき
- 複製ロジックをオブジェクト自身に担当させたいとき
Swiftの値型:代入するだけでコピーが完了
Swiftの構造体(struct)と列挙型(enum)、そしてArray・Dictionary・Stringなどの標準型はすべて値型です。
値型は代入した瞬間、または関数に渡した瞬間にコピーされます。
つまり、言語が自動的にコピーを作ってくれます。
例を見ると、より明確になります。
struct Point { var x: Int; var y: Int }
var a = Point(x: 1, y: 2)
var b = a // この瞬間に値がコピーされる
b.x = 99
// a.xは引き続き 1, b.xだけを 99
b = aこの1行が、プロトタイプパターンのclone()が担っていた役割を代わりに果たします。
別の複製メソッドも、コピー用プロトコルの採用も必要ありません。
元のaは安全に保護され、bは完全に独立したコピーになります。
オブジェクト同士が絡み合うことで起きる厄介なバグが、そもそも発生しない構造なのです。
Copy-on-Writeがパフォーマンス問題を解決する方法
ここで自然な疑問が生まれます。
「代入するたびに丸ごとコピーしたら、大きな配列は遅くなりすぎないでしょうか?」
その指摘はもっともです。そこでSwiftが使う最適化がCopy-on-Write、略してCoWです。
CoWの原理は次のとおりです。
値を代入すると、まず内部ストレージを共有します。実際のデータコピーは後回しにします。
その後、どちらかが値を変更しようとした瞬間に、初めて実際のコピーが行われます。
| タイミング | 内部動作 | コスト |
|---|---|---|
| 代入時 | ストレージを共有(参照のみ) | 非常に低い |
| 読むだけのとき | 共有を維持 | コピーなし |
| 値を変更するとき | この時点で実際にコピー | この時点でのみ発生 |
これにより、開発者は値型の安全性を享受しながら、不要なコピーコストを負わずに済みます。
読むだけで変更しなければ、コピーはまったく発生しないからです。
Array、Dictionary、Set、Stringなどの標準ライブラリ型には、すでにこのCoWが組み込まれています。
自分で実装しなくても、言語と標準ライブラリが自動的に処理してくれます。
それでもプロトタイプパターンが必要になる場合はあるのでしょうか?
「では、Swiftではプロトタイプパターンは完全に廃れた概念なのでしょうか?」
必ずしもそうではありません。
クラス(class)が必要な状況があります。参照セマンティクスが不可欠な場合や、Objective-Cとの相互運用が必要な場合、継承が必要な場合です。
このようなときにクラスインスタンスの独立したコピーが必要なら、今でも複製ロジックを自分で作る必要があります。
SwiftにはNSCopyingプロトコルとcopy()があり、これが実質的にプロトタイプパターンの伝統的な形です。
次のように分けて考えると分かりやすいでしょう。
- 構造体・値型を使う → 代入だけでコピー完了、プロトタイプパターンは不要
- クラスを必ず使う → 必要に応じて複製ロジックを自分で実装、ここでパターンが生き残る
そのため、Swiftコミュニティでは「まず値型を検討する」とよく助言されます。
値型を基本にすれば、複製の問題そのものがなくなるからです。
まとめ
慣れない言語のデザインパターンを無理に持ち込むと、かえってコードが複雑になることがあります。
Swiftでは値型とCopy-on-Writeがプロトタイプパターンの役割を自然に埋めてくれることを覚えておくだけで、複製に関する悩みはずっと軽くなります。
次にオブジェクトをコピーすることになったら、clone()を探す前に「これ、構造体に変えられないかな?」と考えてみてください。きっと役に立ちます。

