Unowned Reference(非所有参照)は、Swiftにおける非所有の参照であり、オブジェクトのretain countを増加させず、weakとは異なり、オブジェクトの解放後にnilに設定されません。Apple Swift Language Guide, 2026によると、unownedは、オブジェクトがそれを参照するオブジェクトと少なくとも同じ期間生きることが保証されている場合に使用されます。Weak Referenceとは異なり、unownedはアンラップを必要としません。これは非オプショナル型であり、コードをよりクリーンにしますが、ライフタイムの保証に関する責任を開発者に負わせます。
重要なポイント
Unowned Referenceは、ARCにおけるオブジェクトへの非所有参照であり、そのretain countを増加させません。weakとは異なり、unowned参照はオブジェクトの解放後にゼロイングされません。解放済みのメモリを指し続けます。このような参照にアクセスすると、EXC_BAD_ACCESSによる実行時クラッシュが発生します。
“非所有”という用語は、その意味合いを反映しています。オブジェクトは存在しますが、そのライフタイムに責任を持つ者は誰もいません。開発者は明示的に宣言します:「このオブジェクトは私が参照している限り生き続けることを保証します。」コンパイラはこの保証を検証しません。これは開発者レベルの契約です。
Swift.org Documentation, 2026によると、unowned参照はライフタイムが保証されたシナリオでweakよりも優先されます。その理由は、オプショナル型を必要とせず(よりクリーンなコード)、アンラップを必要とせず(force-unwrapやguard letが少ない)、ゼロイング用のweakテーブルを維持するオーバーヘッドがないためです。ただし、契約に違反するとクラッシュします。
Swiftでは、unowned参照はletまたはvarの前にキーワードunownedを付けて宣言します。weakとは異なり、unownedはletとvarの両方を使用でき、オプショナル型を必要としません。この特性により、unownedはドメインロジックによってnilになり得ない参照に便利です。
class Country {
let name: String
var capital: City! // 初期化後に設定されます
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — ライフタイム保証
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// 使用法
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — retain cycleなし
この例では、City unowned let country — 都市は国なしでは存在できません。国が消滅すれば、都市(と参照)は意味を失います。意味論的には、これはunownedに理想的なケースです。ライフタイム保証が存在し、オプショナルは不要で、retain cycleは発生しません。
unowned varは許可されていますが、あまり一般的ではありません。参照を置き換える可能性がある場合(例えば、子を別の親に再バインドする場合)に使用されます。再割り当て時に、古いオブジェクトの解放は外部所有者の責任です。
Swift 5.0+では、unownedオプショナル(unowned let x: Type?)のサポートが導入されました。これは妥協案です。unownedは、参照がnilでなければオブジェクトが生きていることを保証します。解放時の動作は通常のunownedと同様にクラッシュです。
unownedとweakの選択は、Swiftアーキテクチャを設計する際の頻繁な判断の一つです。各ケースの基準と推奨事項を検討しましょう。
| 基準 | Weak | Unowned |
|---|---|---|
| オプショナル | あり (Type?) | なし (Type) |
| 解放時のゼロイング | 自動でnilに | なし(ダングリングポインタのリスク) |
| 型(let/var) | varのみ | letまたはvar |
| パフォーマンス | weakテーブルのオーバーヘッド | 最小限(単純なポインタ) |
| 安全性 | 安全(nilチェックあり) | EXC_BAD_ACCESSのリスク |
| ライフタイム保証 | 不要 | 明示的な保証が必要 |
weakを使用するのは、オブジェクトのライフタイムについて少しでも疑問がある場合です。weakは安全で明確であり、証明を必要としません。unownedを使用するのは、オブジェクトがより早く解放される可能性のあるすべてのシナリオを除外できる場合のみです。典型的なケース:親なしでは存在しない子、同期的に実行されるクロージャ、自身のイニシャライザ内でのオブジェクトへのアクセス。
Airbnb Swift Style Guide, 2025によると、大規模なコードベースではデフォルトでweakを使用し、unownedはライフタイム保証を説明する明示的なコメントと共にのみ使用することが推奨されています。これにより、リファクタリング時の不明瞭なクラッシュのリスクが軽減されます。
クロージャは、親子関係に次いでunownedの2番目に頻繁な使用ケースです。キャプチャリスト[unowned self]は、selfがクロージャより長く生きることが保証されている場合に使用されます。正しいシナリオと誤ったシナリオを検討しましょう。
同期クロージャ — sorted、filter、map。これらは現在のスレッドで即座に実行され、selfは確実に生きています。unownedを使用したキャプチャリストはここでは許容され、よりクリーンなコードになります。
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sortedは同期的に実行され、selfは確実に生存
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
非同期クロージャ — 遅延、ネットワークリクエスト、アニメーションを伴うもの。クロージャのスケジューリングと実行の間にselfが解放される可能性があります。この場合、unowned selfはクラッシュにつながります。[weak self]を使用してください。
class NetworkLoader {
func loadData() {
// ❌ 危険: 非同期クロージャでのunowned self
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // selfが解放されるとクラッシュ
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ 正解: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
ルールを覚えておいてください:unowned self — 即座に実行される同期クロージャのみ。非同期クロージャの場合は、常にweak self + guard letを使用してください。例外:クロージャが完了するまでオブジェクトへの参照を明示的に保持する場合(例えば、別の変数に強参照を保持する)。
Unownedは強力だが危険なツールです。unownedがクラッシュにつながる可能性のある実際のシナリオと、リスクを最小化する方法を検討しましょう。
unownedの主なリスクは、ライフタイム保証を無効にするビジネスロジックの変更です。開発者がコードをリファクタリングし、所有権を変更したり、遅延解放を導入したり、キャッシュを追加したりすると、unowned参照が時限爆弾と化します。コンパイラは警告しません — ユーザーのデバイスでクラッシュが発生するだけです。
推奨:unownedは、ライフタイム保証が明白で文書化されている場合にのみ使用してください。各unownedにコメントを追加してください:なぜこの参照が安全なのか、どのような条件下で違反する可能性があるのか。
UIKitはunownedにとって高リスク領域です。ViewControllerは、ナビゲーション(pop、dismiss)、メモリ解放、画面回転の際にいつでも解放される可能性があります。unowned selfを使用してViewControllerをクロージャに渡した場合、バックグラウンドから戻る時やアニメーション完了時にselfがnilになっている可能性があります。
unownedを使用する際のリスクを軽減するには、次のルールに従ってください:
// 例: 明示的な正当性の根拠とともに文書化されたunowned参照
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItemはInvoiceなしでは存在できません。
// InvoiceはItemを作成し、削除時にそれを除去します。
// 保証: InvoiceはItemと少なくとも同じ期間生存します。
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// これは強力な保証です: InvoiceはdeinitですべてのItemを除去します。
// 保証違反 = 修正が必要なビジネスロジックのバグ。
保証の文書化はプロフェッショナルスタンダードです。大規模プロジェクト(Airbnb、Uber)では、コードレビューで各unownedの正当性が求められます。保証が明確でない場合はweakを使用してください。unownedへのコメントは、将来の開発者がなぜここでweakが使用されなかったのか、どのような条件で保証が破られる可能性があるのかを理解するのに役立ちます。
よくある質問
実行時クラッシュ(EXC_BAD_ACCESS)。Swiftはアクセス時にunowned参照の有効性をチェックしません — 単なる「生の」ポインタです。オブジェクトが解放されるとメモリは上書きされ、アクセスすると致命的に終了します。これはキャッチ不可能な例外です(try-catch不可)。
はい、プロトコルがAnyObjectを継承している場合。Unownedはすべての参照型(クラス、AnyObjectプロトコル、Objective-Cオブジェクト)で動作します。値型(struct、enum)はARCに関与しないため、unownedをサポートしません。
ライフタイム保証が絶対的で明白な場合 — unownedは設計の観点からより安全です。アンラップが不要で、nilになり得ず、エラーを隠しません。オブジェクトが親なしで存在できない場合、unownedはそれを明示的な契約にしますが、weakは保証を曖昧にします。
はい:unownedの方が高速です。ゼロイングのために実行時にweakテーブルにアクセスする必要がないためです。ほとんどのアプリケーションでは違いはわかりませんが、何百万ものアクセスがある高負荷シナリオでは、unownedは読み取りで10〜20%高速になる可能性があります。
リファクタリングはunownedの主な危険です。オブジェクトのライフタイムの変更(キャッシュ、非同期操作、再利用)は保証を破る可能性があります。コンパイラは警告しません。解決策:アーキテクチャを変更する際はweakに移行するか、警告コメントを追加してください。
まとめ
unowned letまたはunowned var。非オプショナルおよびオプショナル(Swift 5.0+)可能ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。