Weak Reference(弱参照)とは、ARCにおいてオブジェクトの保持カウントを増加させない参照のことです。Apple Swift Language Guide, 2026によると、弱参照はweakキーワードで宣言され、常にオプショナル型です。オブジェクトが解放されると、そのオブジェクトへのすべての弱参照は自動的にnilに設定され、ダングリングポインタを防止し、弱参照を retain cycle を解消するための安全なメカニズムにします。
重要ポイント
weakキーワード。型は常にオプショナル (?)Weak Referenceは、ARC(Automatic Reference Counting)におけるオブジェクトへの非所有参照です。オブジェクトの retain count を増やして生存期間を保証する強参照とは異なり、弱参照はオブジェクトがまだ参照されていても解放されることを許可します。解放後、弱参照は自動的にnilに設定されます — これをzeroing weakと呼びます。
Zeroing weakは、SwiftおよびObjective-Cランタイムの重要な機能です。オブジェクトの参照カウントがゼロになりオブジェクトが解放されると、ランタイムはこのオブジェクトへのすべての弱参照(専用のweakテーブルに保存されている)を走査し、それらをnilに設定します。これにより、弱参照を介して解放されたメモリにアクセスする(use-after-free)ことは不可能になります — 読み取りは常にnilを返します。
Apple WWDC 2012 Session 406によると、zeroing weak参照は、手動メモリ管理(MRR)で一般的だったダングリングポインタに関連するクラッシュバグのクラス全体を排除しました。MRRでは、弱参照は__unsafe_unretainedとしてのみ存在し、ゼロにならず、解放されたオブジェクトへのアクセスはEXC_BAD_ACCESSを引き起こしていました。
Appleエコシステムの両方の言語で弱参照を宣言する構文を見てみましょう。ランタイムは共通ですが、構文は異なります。ただし、意味は同一です。
Swiftでは、弱参照はvarの前にweakキーワードを付けて宣言します。参照はいつでもnilになる可能性があるため、型は常にオプショナル(Type?)である必要があります。定数(let)をweakにすることはできません — 変数のみ可能です。
class ViewController: UIViewController {
// weak properties: only var, only optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ クロージャはweakを保持しない
// ⬆️ エラー:weakはclass型にのみ適用可能、クロージャには不可
}
重要:weakは、クラスインスタンス(class型)、AnyObject、およびAnyObjectから継承されたプロトコルにのみ適用可能です。Struct、enum、クロージャをweakにすることはできません — これらは値型であり、ARCに関与しません。
Objective-Cでは、弱いプロパティは__weak属性またはプロパティ宣言内のweak修飾子を使用して宣言します:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// ローカルweak変数
__weak MyObject *weakRef = someStrongObject;
Objective-Cランタイムもzeroing weakを提供しますが、さらにC構造体や一部のCore Foundationオブジェクトでのweakの使用をブロックします。これらには、__unsafe_unretainedを使用します — zeroingなしです。
弱参照は普遍的な解決策ではなく、特定のシナリオのためのツールです。いたるところでweakを使用すると、不必要な複雑さが生じ、可読性が損なわれます。正しい使用シナリオを見てみましょう。
デリゲート — weakの主要なシナリオです。所有オブジェクト(例:UITableView)は自身への強参照を保持しますが、デリゲート(UIViewController)はテーブルを所有すべきではありません。Apple SDKは、すべてのデリゲートとdataSourceがweakであることを保証しています。独自のプロトコルでは、常にweak var delegateを使用してください。
子オブジェクトが親を参照する必要がある場合(例:ChildViewControllerがコーディネーターにアクセスする)、弱参照を使用します。親は子を所有し(strong)、子は親を監視します(weak)— retain cycleが排除されます。
Capture list [weak self] — クラスのプロパティとして保存されるクロージャでのretain cycleを回避する標準的な方法です。クロージャが完了する前にselfが解放される可能性がある場合、weak selfは必須です。
| シナリオ | Weak | Strong |
|---|---|---|
| デリゲート | ✅ 常にweak | ❌ Retain cycle |
| 親 → 子 | ❌ 不要(親が所有すべき) | ✅ Strong |
| 子 → 親 | ✅ Weak | ❌ Retain cycle |
| 非同期コールバック | ✅ [weak self] | ❌ Retain cycleのリスク |
| 強結合(owned) | ❌ unowned | ✅ Strong |
一般則:オブジェクトAがBを所有する場合(A → B strong)、B → Aはweakまたはunownedであるべきです。強参照の方向は常に所有者から従属者へ向かうべきです。
weakとunownedはどちらも retain count を増やしませんが、オブジェクト解放後の動作が異なります。両者の選択は生存期間の保証の問題です。
Weak:自動的にnilになります。型は常にオプショナルで、使用前にアンラップが必要です。安全 — nilへのアクセスはクラッシュを引き起こしません。
Unowned:nilになりません。型は非オプショナルです。オブジェクトが解放されると、unowned参照はダングリングポインタになります — アクセスするとランタイムクラッシュが発生します。Unownedは、オブジェクトが参照側と少なくとも同じ期間生存することを前提とします。
Weakを選ぶ場合:オブジェクトがいつでも解放される可能性がある場合(画面閉じた後のデリゲート)、オブジェクトの生存期間を制御できない場合、または保証が不明確な場合。Weakは普遍的な安全な選択です。
Unownedを選ぶ場合:オブジェクトが参照側より先に解放されないことが保証されている場合(例:Customer → CreditCard、カードは顧客なしでは存在しない)。Unownedはアンラップなしの非オプショナルAPIを提供し、コードでより便利です。
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// 強い関係:OrderがItemを所有
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — ItemはOrderなしでは生存不可
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// weakの例:生存期間保証なしのデリゲート
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — デリゲートは消える可能性あり
}
例では、Itemはunownedを使用しています。注文アイテムは注文自体なしでは存在できないためです — 生存期間の保証は確固たるものです。NetworkServiceはweakを使用しています。デリゲート(例:ViewController)はいつでも閉じられ解放される可能性があるためです。
弱参照は強力なツールですが、iOS開発で正しく使用するために理解すべき制限があります。
弱参照は強参照より遅い:アクセスごとに、ランタイムはオブジェクトが解放されたかどうかをチェックします(weakテーブルのルックアップ)。ほとんどのシナリオでは違いは感じられませんが、数百万回のアクセスがあるホットループでは、weakがボトルネックになる可能性があります。高負荷シナリオでは、strongを使用してアーキテクチャを再編成してください。
Struct、enum、tuple — ARCに関与しない値型です。weak structを宣言しようとするとコンパイルエラーになります。値型への弱参照を保存するには、クラス型のラッパーまたはクロージャを使用してください。
Zeroing weakはスレッドセーフです:あるスレッドでオブジェクトが解放されると、すべてのスレッドで弱参照がアトミックにゼロになります。ただし、弱参照を読み取ってから参照を外すまでの間に競合状態が発生する可能性があります — 弱参照の取得と使用の間にオブジェクトが解放されます。解決策:弱参照をローカル変数にstrongキャプチャします。
// マルチスレッドでのweakの競合状態
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelfはチェックと使用の間にnilになる可能性あり
if weakSelf != nil {
weakSelf!.doSomething() // nilになるとCRASH
}
}
}
// ✅ 修正:使用中はstrongキャプチャ
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — ローカルstrong参照
}
}
安全なバージョンでは、weak selfがキャプチャされ、すぐにローカルのstrong変数strongSelfにアンラップされます。selfがまだ生きている場合、ブロックの実行中は生存し続けます。そうでない場合、guardが作動してコードは実行されません。このイディオムは、Swiftにおける非同期クロージャの標準的なパターンです。
IBOutletはInterface Builderではweakであるべきです。なぜなら、ビュー階層がすでにサブビューへの強参照を保持しているからです。コントローラーで強参照を複製してもretain cycleは発生しませんが、冗長です。アウトレットへの弱参照はAppleの推奨ですが、多くの開発者はコードの簡素化のためにstrongを使用しています。
よくある質問
いいえ、weakは既存のオブジェクトまたはnilのみを指すことができます。新しいオブジェクトを作成する際は、まず強参照を取得し(イニシャライザを介して)、その後にのみ弱参照を割り当てることができます。最初にweakがnilであることは正常な状態です。
WeakはARCに基づいており、ARCは参照型(クラス)のみを管理します。値型(struct、enum)は代入時にコピーされ、retain countを持ちません。値型との弱い関係には、weakプロパティを持つクラスのラッパーまたはクロージャを使用してください。
弱参照への各アクセスはランタイムテーブルでルックアップを実行します。数百万回の反復があるループでは、強参照よりも2〜5倍遅くなる可能性があります。ホットパスでは、ループの前にweakをローカルのstrong変数にコピーしてください。
オブジェクトへのすべての強参照が失われたとき — スコープの終了時、プロパティの再割り当て時、画面が閉じられたときです。マルチスレッド環境では、これはコードの2行の間で発生する可能性があります。常にguard letまたはif letで弱参照を確認してください。
意味的には同一です:どちらもzeroing weakを提供します。違い:Swiftはオプショナル型とvarを必要とし、Objective-Cはプロパティ修飾子を使用します。Objective-Cは__unsafe_unretainedもサポートしています — zeroingなしの弱参照(ダングリングポインタのリスク)。
まとめ
weak var + オプショナル型。class型とAnyObjectプロトコルのみターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。