Swift & Objective-C

Swift ARC:循環参照とweak・unownedの寿命を確認する

強参照の循環を作って切断し、weakがnilになる過程を実行します。unownedのオプショナルと自動的なnil化の違いも確認します。

読了 3 分
Swift ARC:循環参照とweak・unownedの寿命を確認するのカバー画像

weakとunownedは対象を所有しませんが、解放後の挙動が違います。weakはnilになり、安全なunownedで解放済みの対象にアクセスすると実行時エラーになります。選択基準は構文ではなく寿命の保証です。

仕組みと設計の判断基準

ARCはコンパイラーが生成する所有権操作と、ランタイムの参照管理が連携する仕組みです。強い循環参照では外部の所有者が消えてもオブジェクトが残ります。unownedもオプショナルにできますが、生存中の対象か明示的なnilに保つ責任があります。 一次資料

再現コード

最初は二つのNodeを互いに所有させ、外部の強参照をなくし、weakで生存を観察して一辺を切ります。次は親から子へ強参照、子から親へweakを使います。最後はOwnerが生存中にunownedオプショナルを手動でnilへ変えます。

この独立したサンプルは2026-10-23に、arm64 macOS向けのApple Swift 6.4をSwift 6言語モードで実行しました。ネットワーク接続や追加パッケージは不要です。

Swiftファイル全体をダウンロード

ダウンロード先のフォルダーで実行してください。検証条件を満たさない場合はpreconditionで停止します。検証が削除されるため、-Ouncheckedは使用しないでください。

swift -swift-version 6 swift-arc-weak-unowned.swift
final class Node { var next: Node? }
func breakCycle(_ node: Node) { node.next = nil }

weak var observedA: Node?
weak var observedB: Node?
do {
    let a = Node()
    let b = Node()
    a.next = b
    b.next = a
    observedA = a
    observedB = b
}
precondition(observedA != nil && observedB != nil)
breakCycle(observedA!)
precondition(observedA == nil && observedB == nil)

final class Owner { var child: Child? }
final class Child { weak var owner: Owner? }
weak var observedOwner: Owner?
weak var observedChild: Child?
do {
    let owner = Owner()
    let child = Child()
    owner.child = child
    child.owner = owner
    observedOwner = owner
    observedChild = child
}
precondition(observedOwner == nil && observedChild == nil)

final class Borrower {
    unowned var owner: Owner?
    init(_ owner: Owner) { self.owner = owner }
}
do {
    let owner = Owner()
    let borrower = Borrower(owner)
    withExtendedLifetime(owner) {
        precondition(borrower.owner === owner)
        borrower.owner = nil
        precondition(borrower.owner == nil)
    }
}
print("cycle=retained broken=released weak=nil unowned-optional=manually-cleared")

実行結果と読み取り方

cycle=retained broken=released weak=nil unowned-optional=manually-cleared

強参照の循環はブロック終了後も残りましたが、一辺を切ると両方の観察値がnilになりました。逆向きにweakを使う親子は強参照がなくなると解放されました。unownedオプショナルのnilは自動処理ではなく明示的な代入の結果です。

適用範囲と注意点

このコードは解放済みunownedへのアクセスを実行せず、そのクラッシュを実測したとも主張しません。特定のソース行で必ず解放されるという一般化もできません。最適化や寿命延長が影響します。deinitログがないだけでは、キャッシュなどの正当な所有とリークを区別できません。

次に確認すること

所有グラフを描き、逆方向の関係が所有すべきか判断します。対象の消滅が正常ならweak、アクセス中の生存が保証されるならunownedを検討します。保存したクロージャが所有者を保持する場合はキャプチャの実験へ進みます。

関連する実験

出典と確認基準