ようこそ
構造体には、保存プロパティとしてクラスを含めることができます。この場合、構造体は外側から見ると値型として振る舞いますが、内部ではクラス参照を共有します。これは見落としやすい落とし穴です。
クラスを含む構造体
```swift
class Storage { var data: [Int] = [] }
struct Container {
var storage = Storage()
var label: String = ""
}
var a = Container()
var b = a // copies label, but SHARES storage!
a.storage.data.append(1)
print(b.storage.data) // [1] — shared!
```
コピーされるのは値型のプロパティだけ
構造体をコピーすると:
• 値型のプロパティ(`label: String`)は完全にコピーされます
• 参照型のプロパティ(`storage: Storage`)は、ポインターだけがコピーされます
これは*浅いコピー*と呼ばれます。
ディープコピーを作成する
```swift
struct Container {
var storage: Storage
func deepCopy() -> Container {
let newStorage = Storage()
newStorage.data = storage.data // copy values
return Container(storage: newStorage, label: label)
}
}
```
COW が解決します
Swift 標準のコレクションは、COW を使うことでこの問題を回避しています:
```swift
struct SafeContainer {
private(set) var storage = Storage()
mutating func append(_ n: Int) {
if !isKnownUniquelyReferenced(&storage) {
storage = storage.copy() // copy before mutating
}
storage.data.append(n)
}
}
```
構造体を含むクラス
```swift
class ViewModel {
var point = CGPoint(x: 0, y: 0) // value type inside reference type
}
let vm1 = ViewModel()
let vm2 = vm1 // same ViewModel!
vm1.point.x = 5
print(vm2.point.x) // 5 — shared because ViewModel is a class
```
アイデンティティと等価性
```swift
class Dog { var name: String = "" }
let x = Dog()
let y = x
print(x === y) // true — same object
print(x !== y) // false
let z = Dog()
print(x === z) // false — different objects
```
クラスを介した再帰的な構造体
構造体は自分自身を直接参照できませんが、構造体を参照するクラスを含めることはできます:
```swift
struct Tree {
var value: Int
var children: [Tree] // OK — Array uses class internally
}
```
予期しない共有の検出
実行時に共有を検出するには、`isKnownUniquelyReferenced` を使います:
```swift
var s = Storage()
var r: AnyObject = s
print(isKnownUniquelyReferenced(&s)) // true
var s2 = s
print(isKnownUniquelyReferenced(&s)) // false — shared
```
設計ガイドライン
構造体が可変な参照型の状態を保持する必要がある場合:
1. COW を実装して値セマンティクスを取り戻します
2. または不変なクラスを使います。クラスプロパティには `let` と `var` のどちらを使うか検討してください
3. または設計を見直します。構造体ではなくクラスにすべきかもしれません
共有が意図的である場合は、明確に文書化してください。
クイックチェック
クラスプロパティを含む構造体をコピーすると、クラスには何が起こりますか。
まとめ
重要なポイント:
• 構造体のコピーは浅いコピーです。クラス参照は共有されます
• `isKnownUniquelyReferenced` は実行時に共有を検出します
• COW を実装して、本来の値セマンティクスを取り戻してください
• `===` / `!==` はクラスのオブジェクトアイデンティティを検査します
• 再帰的な構造には、子要素として Array(クラスを内部で使用)を使います
次は、値セマンティクスを持つ API の設計です。