Weak Reference(弱参照)とは — モバイル開発における構文と使用方法

著者: IT Sectr 公開日: 2026-03-30 読了時間: 9 分

Weak Reference(弱参照)とは、ARCにおいてオブジェクトの保持カウントを増加させない参照のことです。Apple Swift Language Guide, 2026によると、弱参照はweakキーワードで宣言され、常にオプショナル型です。オブジェクトが解放されると、そのオブジェクトへのすべての弱参照は自動的にnilに設定され、ダングリングポインタを防止し、弱参照を retain cycle を解消するための安全なメカニズムにします。

重要ポイント

  • Weak Reference — オブジェクトの retain count に影響しない参照。オブジェクト解放時にnilになる
  • 宣言 — varの前にweakキーワード。型は常にオプショナル (?)
  • 用途 — デリゲート、クロージャ、親子関係でのretain cycle解消
  • 安全性 — オブジェクト解放後に自動的にnilに設定(zeroing weak)
  • unownedとの違い — weakはnilになり安全、unownedはnilにならず生存期間の保証が必要

Weak Referenceとは?

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を引き起こしていました。

SwiftとObjective-Cにおけるweakの構文

Appleエコシステムの両方の言語で弱参照を宣言する構文を見てみましょう。ランタイムは共通ですが、構文は異なります。ただし、意味は同一です。

Swift

Swiftでは、弱参照はvarの前にweakキーワードを付けて宣言します。参照はいつでもnilになる可能性があるため、型は常にオプショナル(Type?)である必要があります。定数(let)をweakにすることはできません — 変数のみ可能です。

swift
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

Objective-Cでは、弱いプロパティは__weak属性またはプロパティ宣言内のweak修飾子を使用して宣言します:

objective-c
// 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を使用すると、不必要な複雑さが生じ、可読性が損なわれます。正しい使用シナリオを見てみましょう。

デリゲート(Delegateパターン)

デリゲート — weakの主要なシナリオです。所有オブジェクト(例:UITableView)は自身への強参照を保持しますが、デリゲート(UIViewController)はテーブルを所有すべきではありません。Apple SDKは、すべてのデリゲートとdataSourceがweakであることを保証しています。独自のプロトコルでは、常にweak var delegateを使用してください。

逆参照を持つ親子関係

子オブジェクトが親を参照する必要がある場合(例:ChildViewControllerがコーディネーターにアクセスする)、弱参照を使用します。は子を所有し(strong)、子は親を監視します(weak)— retain cycleが排除されます。

非同期クロージャ

Capture list [weak self] — クラスのプロパティとして保存されるクロージャでのretain cycleを回避する標準的な方法です。クロージャが完了する前にselfが解放される可能性がある場合、weak selfは必須です。

シナリオWeakStrong
デリゲート✅ 常にweak❌ Retain cycle
親 → 子❌ 不要(親が所有すべき)✅ Strong
子 → 親✅ Weak❌ Retain cycle
非同期コールバック✅ [weak self]❌ Retain cycleのリスク
強結合(owned)❌ unowned✅ Strong

一般則:オブジェクトAがBを所有する場合(A → B strong)、B → Aはweakまたはunownedであるべきです。強参照の方向は常に所有者から従属者へ向かうべきです。

Weak vs Unowned:比較とシナリオ

weakとunownedはどちらも retain count を増やしませんが、オブジェクト解放後の動作が異なります。両者の選択は生存期間の保証の問題です。

違い

Weak:自動的にnilになります。型は常にオプショナルで、使用前にアンラップが必要です。安全 — nilへのアクセスはクラッシュを引き起こしません。

Unowned:nilになりません。型は非オプショナルです。オブジェクトが解放されると、unowned参照はダングリングポインタになります — アクセスするとランタイムクラッシュが発生します。Unownedは、オブジェクトが参照側と少なくとも同じ期間生存することを前提とします。

Weakを選ぶべき場合

Weakを選ぶ場合:オブジェクトがいつでも解放される可能性がある場合(画面閉じた後のデリゲート)、オブジェクトの生存期間を制御できない場合、または保証が不明確な場合。Weakは普遍的な安全な選択です。

Unownedを選ぶべき場合

Unownedを選ぶ場合:オブジェクトが参照側より先に解放されないことが保証されている場合(例:Customer → CreditCard、カードは顧客なしでは存在しない)。Unownedはアンラップなしの非オプショナルAPIを提供し、コードでより便利です。

swift
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テーブルのルックアップ)。ほとんどのシナリオでは違いは感じられませんが、数百万回のアクセスがあるホットループでは、weakがボトルネックになる可能性があります。高負荷シナリオでは、strongを使用してアーキテクチャを再編成してください。

Weakは値型に適用不可

Struct、enum、tuple — ARCに関与しない値型です。weak structを宣言しようとするとコンパイルエラーになります。値型への弱参照を保存するには、クラス型のラッパーまたはクロージャを使用してください。

マルチスレッドでのWeak

Zeroing weakはスレッドセーフです:あるスレッドでオブジェクトが解放されると、すべてのスレッドで弱参照がアトミックにゼロになります。ただし、弱参照を読み取ってから参照を外すまでの間に競合状態が発生する可能性があります — 弱参照の取得と使用の間にオブジェクトが解放されます。解決策:弱参照をローカル変数にstrongキャプチャします。

swift
// マルチスレッドでの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における非同期クロージャの標準的なパターンです。

UIViewとWeak Outlet

IBOutletはInterface Builderではweakであるべきです。なぜなら、ビュー階層がすでにサブビューへの強参照を保持しているからです。コントローラーで強参照を複製してもretain cycleは発生しませんが、冗長です。アウトレットへの弱参照はAppleの推奨ですが、多くの開発者はコードの簡素化のためにstrongを使用しています。

よくある質問

弱参照はまだ作成されていないオブジェクトを指すことができますか?

いいえ、weakは既存のオブジェクトまたはnilのみを指すことができます。新しいオブジェクトを作成する際は、まず強参照を取得し(イニシャライザを介して)、その後にのみ弱参照を割り当てることができます。最初にweakがnilであることは正常な状態です。

なぜweakはclass型でのみ動作するのですか?

WeakはARCに基づいており、ARCは参照型(クラス)のみを管理します。値型(struct、enum)は代入時にコピーされ、retain countを持ちません。値型との弱い関係には、weakプロパティを持つクラスのラッパーまたはクロージャを使用してください。

Weakはループ内のパフォーマンスにどのように影響しますか?

弱参照への各アクセスはランタイムテーブルでルックアップを実行します。数百万回の反復があるループでは、強参照よりも2〜5倍遅くなる可能性があります。ホットパスでは、ループの前にweakをローカルのstrong変数にコピーしてください。

弱参照が予期せずnilになるのはいつですか?

オブジェクトへのすべての強参照が失われたとき — スコープの終了時、プロパティの再割り当て時、画面が閉じられたときです。マルチスレッド環境では、これはコードの2行の間で発生する可能性があります。常にguard letまたはif letで弱参照を確認してください。

WeakとObjective-Cの__weakの違いは何ですか?

意味的には同一です:どちらもzeroing weakを提供します。違い:Swiftはオプショナル型とvarを必要とし、Objective-Cはプロパティ修飾子を使用します。Objective-Cは__unsafe_unretainedもサポートしています — zeroingなしの弱参照(ダングリングポインタのリスク)。

まとめ

  • Weak Reference — retain countを増やさず、解放時に自動的にゼロになる非所有参照
  • 構文weak var + オプショナル型。class型とAnyObjectプロトコルのみ
  • Zeroing weak — ランタイムが解放されたオブジェクトへのすべての弱参照をゼロにし、ダングリングポインタを防止
  • シナリオ — デリゲート、逆参照付き親子、非同期クロージャ([weak self])
  • Weak vs Unowned — weakはnilになる(安全)、unownedはnilにならない(クラッシュのリスク、ただし非オプショナル)
  • パフォーマンス — weakはランタイムテーブルのルックアップによりstrongより遅い。ホットパスではstrongにコピー
  • 推奨 — 生存期間の保証が不明確な場合はweakを選択

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください