Strong Reference(強い参照):その概要、動作メカニズム、ARC

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

Strong Reference(強い参照)は、対象が少なくとも1つのアクティブな参照によって指されている限り、メモリに残り続ける標準的なメモリ管理メカニズムです。弱い参照とは異なり、強い参照はオブジェクトの参照カウントを増やし、自動的な解放を防ぎます。Apple Developer Documentationによると、ARCはSwiftやObjective-Cにおけるオブジェクトのライフサイクルを自動的に管理します。モバイルアプリケーションにおけるメモリリークやサイクル依存関係を防止するには、強い参照の仕組みを理解することが重要です。

重要なポイント

  • Strong Reference — retain countを1増やしてオブジェクトをメモリに保持する参照。
  • ARCは自動的にretainとrelease操作を挿入し、SwiftやObjective-Cでの手動メモリ管理を不要にします。
  • Retain cycleは、2つのオブジェクトが互いに強い参照を持ち合うときに発生し、メモリが解放されなくなります。
  • Weak Referenceは参照カウントを増やさず、オブジェクトが解放されると自動的にnilになります。
  • Unowned Referenceはカウントを増やさないが、オブジェクトが所有者よりも長く生きないことを前提とします。

Strong Referenceとは?

Strong Referenceは、ガベージコレクタやメモリ管理システムによるオブジェクトの破壊を防ぐ参照の一種です。対象に対して少なくとも1つの強い参照が存在する限り、そのメモリは解放されません。これは、SwiftやObjective-CのARC、およびJavaやKotlinのガベージコレクションの基礎となるメカニズムです。

強い参照の概念は、自動メモリ管理を備えたすべての言語において基本的なものです。ARCを使用するシステムでは、それぞれの強い参照がオブジェクトの参照カウントを増やします。カウントがゼロになると、オブジェクトは即時にデアロケートされます。ガベージコレクションを使用するJavaやKotlinでは、強い参照によってオブジェクトがアクセス可能であり、GCによって回収されないことが保証されます。

WWDC 2021によると、iOSアプリケーションにおけるメモリリークの約35%が、強い参照の不正な使用やretain cyclesに関連しています。Android開発では、closuresやcallbacksでの暗黙的な強い参照を通じたリークが、Context Leakに次いで302番目によくあるメモリ問題の原因です。

メモリを効率的に操作するには、strong、weak、unowned参照の違いを理解し、所有権とオブジェクトのライフタイムに基づいて適切な参照タイプを選択する必要があります。

ARCがメモリ管理をどのように変えたか

ARCの前は、開発者はオブジェクトごとに手動でretainやreleaseを呼び出す必要があり、多くのエラーを引き起こしていました。LLVM 3.0とともに2011年にAppleが導入したARCは、コンパイル時に所有グラフを解析することでこのプロセスを自動化しました。コンパイラが必要な場所でretain、release、autorelease呼び出しを自動的に挿入します。

Clang Static Analyzerによると、ARCの導入によってiOSアプリケーションのメモリ関連バグが70%減少しました。開発者にとって、メモリ管理がより安全になったが、同時に、retain cyclesを避けるために強い参照が内部でどのように動作するかを理解する必要が生じました。

KotlinやJavaでは、ガベージコレクタがARCの役割を果たしますが、強い参照の原則は変わりません:GC Rootsは、強い参照によってオブジェクトが保持される入口です。オブジェクトがGC Rootから強い参照の連鎖を通じてアクセス可能である限り、それは回収されません。

ARCにおけるStrong Referenceの仕組み

ARC(Automatic Reference Counting)は、ヒープ内の各オブジェクトの参照をカウントすることで動作します。オブジェクトへの新しい強い参照が作成されると、カウンタが増加します(retain)。参照が破壊または上書きされると、カウンタが減少します(release)。カウンタがゼロに達すると、オブジェクトは即時にメモリから削除されます。

Swiftの例を考えてみましょう。クラスのインスタンスが作成されると、ARCがメモリを割り当て、retain countを1に設定します。別の変数への新たな代入ごとにカウンタが増えます。変数がスコープから外れると、カウンタが減ります:

swift
class ProfileViewController {
    var nameLabel: String?
    var avatarImage: UIImage?

    func loadProfile() {
        // 新しいインスタンスのeretain count = 1
        let user = User(name: "Ivan")
        // nameLabel設定後のretain count = 2
        nameLabel = user.name
        // メソッドを出る — userがスコープから外れる、retain count = 1
    }
}

このコードでは、ARCによって、Userオブジェクトに対して少なくとも1つの強い参照が存在する限り、メモリに残り続けることが保証されます。loadProfile関数が終了すると、ローカル変数userは破壊されますが、nameLabelはまだオブジェクトを保持しています。メモリが解放されるのは、nameLabelが不存になるか上書きされた場合だけです。

Kotlinでは、似たような動作がGC Rootsを通じて提供されます。ガベージコレクタのルート(例:静的フィールドやアクティブナスレッド)からの強い参照のチェーンが存在する限り、オブジェクトはメモリに残り続けます。違いは、GCが即時にメモリを解放しないことです—アクセス可能性解析後に非同期的に行われます。

メモリ解放が生じるタイミング

ARCでは、カウンタがゼロに達した時点で解放が同期的に行われます。SwiftやObjective-Cでは、オブジェクトが削除される瞬間を正確に知ることができます。KotlinやJavaでは、解放のタイミングが不確実ですが、その代わりにガベージコレクタレベルでのサイクル依存関係を検出するためのより柔軟なスキームが提供されています。

Retain Cyclesとメモリリーク

Retain cycle(保持サイクル)とは、2つ以上のオブジェクトが互いに強い参照を持ち合う状態です。その結果、retain countがゼロになることがなく、オブジェクトがアプリケーションに不要になった後でもメモリが解放されません。

経典的な例:親ビューコントローラが強い参照で子オブジェクトを保持し、その子がまた強い参照で親を保持します。これは、デリゲート、closures、ネストされたlambda式の状況でよく見られます。Instruments Leaksによると、ARCを使用するアプリケーションにおけるメモリリークのうち60%までがretain cyclesが原因です。

swift
class ParentViewController: UIViewController {
    var child: ChildViewController?

    func setupChild() {
        child = ChildViewController()
        // retain cycle: 親が子を保持し、子がclosureを通じて親を保持する
        child?.onEvent = {
            self.handleEvent()
        }
    }

    func handleEvent() {}
}

ここでの問題は、onEvent closureが強い参照でself(ParentViewController)をキャプチャし、ParentViewController自体が強い参照でchildを保持していることです。両方のオブジェクトが解放されることはありません。解決策は、closure内でweak selfを使用してサイクルを解消することです。

Kotlinでは、外部オブジェクトをキャプチャするlambdaを使用すると、似たようなサイクルが発生します。JVMガベージコレクタは、オブジェクトがGC Rootsからアクセス不可能である場合に限り、こうしたサイクルをイベント検出できます。サイクルがアクティブナスレッドやUIコンテキストに絡まっている場合、リークはアプリケーションのライフタイム終了まで続きます。

Strong vs Weak vs Unowned Reference

参照タイプの違いを理解することは、安全なメモリ管理の鍵です。Strong Referenceはretain countを増やします。Weak Referenceはretain countを増やさず、オブジェクトがデアロケートされると自動的にnilになります。Unowned Referenceもretain countを増やしませんが、ゼロにはなりません—デアロケート後にアクセスするとクラッシュします。

参照タイプRetain count安全性使用すべきタイミング
Strong+1安全(デフォルト)オブジェクトの所有権、親 → 子の関係
Weak変化せず自動ゼロ掲沈(安全)デリゲート、callbacks、逆参照
Unowned変化せず遅期アクセス時のクラッシュリスクオブジェクトが所有者より長く生きることが保証されている場合

参照タイプの選択は、所有権関係によって決定されます。オブジェクトBがAの一部であり、Aなしでは存在できない場合—Strongを使用します。Bが独立して存在でき、通知のためにAを参照する場合—Weakを使用します。Unownedはめったに使われることはありません—子オブジェクトのライフタイムが親のライフタイムを超えない場合のみです。

実践的な選択ルール

Apple Developer Documentationが推奨するには、デフォルトではすべての所有関係にstrongを使用してください。retain cycleを避ける必要がある場合は、どちらの参照をweakにすべきかを判断してください。通常は、階層構造における逆参照(子 → 親)がweakになります。Kotlinでは、java.lang.refのWeakReferenceが似た役割を果たし、キャッシュやobserverパターンに使用されます。

強い参照の問題を修正する方法

Retain cyclesの検出が第1歩です。第2歩は、それらを正しく解消することです。強い参照サイクルを解く主な手段は、一方の参照をweakまたはunownedに置き換えることです。ガベージコレクションや言語では、各アクセス前に手動でnullチェックを行うWeakReferenceが追加で使用されます。

SwiftやObjective-Cでは、最もよくある修正は、closuresに[weak self]を追加することです。これによって、デアロケート後にclosureがオブジェクトを保持しないことが保証されます。Kotlinでは、同様の目的でWeakReferenceラッパーやonDestroyでの明示的な参照クリアが使用されます。

swift
class NetworkService {
    func fetchData(completion: @escaping (Data?) -> Void) {
        // weak selfによるキャプチャ — retain cycleを解消
        URLSession.shared.dataTask(
            with: URL(string: "https://api.example.com")!
        ) { [weak self] data, response, error in
            guard let self else { return }
            completion(data)
        }.resume()
    }
}

この例では、[weak self]によって、NetworkServiceが不要になった後にclosureによって保持されないことが保証されます。リクエストが完了する前にselfがデアロケートされた場合は、guard let self else { return }がcompletionを呼ばずにclosureから退出します。

Retain cyclesの診断には、iOSではInstruments Leaksを、AndroidではAndroid Profiler + LeakCanaryを使用してください。これらのツールは、正確な保持グラフを表示し、どの強い参照がオブジェクトの解放を防いでいるかを指示します。定期的なメモリプロファイリングは、あらゆるモバイルプロジェクトのCI/CDパイプラインの一部であるべきです。

SwiftとKotlinにおけるStrong Referenceの比較

SwiftKotlinは、根本的に異なるメモリ管理メカニズムを使用しますが、強い参照の概念は両方に存在します。Swiftは、retain count = 0での同期的な解放を伴うARCを使用します。Kotlinは、アクセス不可能なオブジェクトを非同期的にクリアするトレースGCを使用します。

パラメータSwift (ARC)Kotlin (JVM GC)
メカニズム参照カウント(retain count)アクセス可能性トレース(GC Roots)
解放同期的(カウンタがゼロの場合)非同期的(GCサイクルによる)
Retain cycle自動検出なしGCが検出可能だが、即時ではない
弱参照weak(自動ゼロ掲沈)WeakReference(手動チェック)

主な実践的な違い:Swiftでは、retain cycleは確実なリークです。Kotlinでは、オブジェクトがルートからアクセス不可能な場合、GCがサイクルを解消できますが、リークしたオブジェクトのライフタイムは不確実のままです。したがって、両言語において、最も良いストラテジーは、設計階段で強い参照サイクルを避けることです。

Swiftでは、デリゲートパターンやclosuresでweakを使用してください。Kotlinでは、WeakReferenceまたは所有者が破壊されたときに自動的に参照をクリアするLifecycle-awareコンポーネントを使用してください。両方のアプローチで、目的は同じです—不可変な保持チェーンが生じる強い参照を排除することです。

よくある質問

Strong ReferenceとWeak Referenceの違いは何ですか?

Strong Referenceはオブジェクトのretain countを増やし、参照が存在する限り解放を防ぎます。Weak Referenceはretain countを変えず、オブジェクトがメモリから削除されると自動的にnilになります。強い参照は所有権に、弱い参照は逆コネクションやデリゲートに使用されます。

retain cycleとは何で、なぜ危険なのですか?

Retain cycleは、2つのオブジェクトが互いに強い参照を持ち合う相互ロックです。それらのretain countがゼロになることがなく、メモリが解放されません。これによりメモリリークが発生し、オブジェクトがヒープに残り続け、アプリケーションはより多くのリソースを消費し、最穂的にOutOfMemoryでクラッシュします。

iOSアプリでretain cycleを検出するにはどうすればいいですか?

XcodeのInstruments Leaksを使用します—Leaksテンプレートでプロファイリングを実行し、アプリでシナリオを実行し、リーク指標を確認します。正確な診断には、Cycles & Rootsタブに切り替えると、不可変なサイクルを形成する相互強参照のグラフが表示されます。

Weakの代わりにUnownedを使うべきはどういう場合ですか?

Unownedは、子オブジェクトのライフタイムが親のライフタイムを超えないことが保証されている場合に使用します—例えば、オブジェクトを厳密に定義されたスコープにバインドする場合です。不安な場合はWeakを使用してください。解放されたunowned参照にアクセスするとアプリケーションがクラッシュします。

強い参照はアプリケーションのパフォーマンスに影響しますか?

間接的には影響します。ARCでの各retainおよびreleaseは、オーバーヘッドを伴う原子操作です。サイクル内の多くのオブジェクトは、パフォーマンスに影響を与える可能性があります。しかし、主な問題はARCの速度ではなく、参照タイプの間違った選択によるメモリリークです。

まとめ

  • Strong Referenceは、retain countを増やすことでオブジェクトをメモリに保持する、オブジェクト所有の基本的なメカニズムです。
  • ARCはSwiftやObjective-Cでのメモリ管理を自動化し、手動のretainやreleaseを不要にしますが、retain cyclesからは守りません。
  • Retain cycleは互いの強い参照で発生し、ARCシステムでのメモリリークの主な原因です。
  • WeakおよびUnowned参照は、retain countを増やさずに強い参照サイクルを解消します。
  • 参照タイプの選択は所有関係によって決定されます。觪→子にはStrong、子→觪にはWeakまたはUnownedを使用します。
  • Instruments LeaksLeakCanaryは、iOSやAndroidで問題のある強い参照を検出する主なツールです。
  • 事前に所有グラフを設計することで、アプリリリース後のメモリリーク修正よりもコストを低く抑えられます。

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

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

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

こちらもお読みください