ARCとは:iOSにおけるAutomatic Reference Countingの動作原理

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

Automatic Reference Counting(ARC)は、SwiftおよびObjective-Cにおけるメモリ管理システムであり、各オブジェクトへの参照数を自動的にカウントし、カウンタがゼロになるとオブジェクトを解放します。Apple Swift Documentation、2026によると、ARCはコンパイラに組み込まれ、コンパイル時に動作し、適切な場所にretain/release呼び出しを挿入します。Garbage Collectionとは異なり、ARCは別のコレクタスレッドを必要とせず、アプリケーション実行中に一時停止を発生させません。

重要なポイント

  • ARC — Automatic Reference Counting、SwiftおよびObjective-Cにおけるコンパイラベースのメモリ管理システム
  • 動作原理 — 各オブジェクトには参照カウンタ(retain count)があり、ゼロになるとオブジェクトは即座に解放される
  • 修飾子 — strong、weak、unownedは、参照がカウンタとオブジェクトのライフサイクルにどのように影響するかを決定する
  • GCとの違い — ARCはコンパイル時に決定論的に動作し、Stop-The-Worldの一時停止やバックグラウンドのコレクタスレッドがない
  • Retain Cycle — ARCの主な問題:2つのオブジェクトがstrongを介して相互参照すると、カウンタが決してゼロにならない

ARCとは?

ARC(Automatic Reference Counting)は、AppleがXcode 4.2(2011)でObjective-C向けに導入し、Swiftが継承したコンパイラベースのメモリ管理メカニズムです。手動メモリ管理(Manual Retain-Release、MRR)とは異なり、ARCはretain、release、autoreleaseの呼び出しを完全に自動化し、開発者の介入なしにコンパイル時に挿入します。

ARCはガベージコレクタではありません。動的コード挿入を伴う静的解析です。コンパイラはオブジェクトのライフタイムを解析し、オブジェクトが作成、コピー、またはスコープ外になるポイントにretain/releaseを配置します。結果は決定論的なメモリ解放です:オブジェクトは、遅延や一時停止なしに、参照がなくなった正確なタイミングで削除されます。

WWDC 2011 Session 323によると、MRRからARCへの移行により、Appleアプリケーションにおけるメモリ関連のクラッシュバグが70%削減されました。開発者は手動でretain/releaseのバランスを取る必要がなくなり、リークやdouble-freeエラーのクラス全体が排除されました。

Automatic Reference Countingの仕組み

メモリ内の各オブジェクトには参照カウンタ(retain count)があります。オブジェクトが作成されると、カウンタは1に設定されます。新しいstrong参照がオブジェクトを指すと、カウンタが増加し(retain)、strong参照がなくなるとカウンタが減少します(release)。ゼロに達すると、オブジェクトは即座に解放されます。

Swiftコンパイラは、すべての代入でretain/releaseを挿入するわけではありません。最適化のために静的解析を使用します。たとえば、オブジェクトが渡された後に使用されないことが保証されている場合、コンパイラは不要なrelease/retainをスキップできます。この最適化はARC Optimizationと呼ばれます。

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) initialized (retain count: 1)")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // qがスコープ外になる
    // retain count = 1
    // pがスコープ外になる
    // retain count = 0 → deinit
}

この例は、ARCがカウンタをどのように管理するかを示しています:q = pを代入するとカウンタが増加し、qがスコープ外になると減少します。最後のstrong参照がなくなると、デイニシャライザが即座に呼び出されます。ガベージコレクタは待機しません。メモリはすぐに解放されます。

ARC vs Garbage Collection:主な違い

ARCとGarbage Collectionは同じ問題(自動メモリ管理)を解決しますが、根本的に異なるアプローチを取ります。どちらを選択するかは、言語のアーキテクチャを決定します:Swift(ARC)vs Java/Go(GC)。主な違いを見てみましょう。

特徴ARC(Swift/ObjC)GC(Java/Go)
解放のタイミング決定論的:カウンタがゼロになると即座に非決定論的:次のコレクションサイクルで
実行の一時停止なし(retain/releaseはコンパイル時に挿入)Stop-The-Worldの一時停止(2~200ミリ秒)
オーバーヘッド参照ごとのカウンタの増加/減少オブジェクトグラフの走査、マーキング、スイープ
問題点Retain Cycle(手動解決)ヒープの断片化、忘れられた参照によるリーク
追加スレッド不要ガベージコレクタスレッドが必要

主要なトレードオフ:ARCは予測可能なオブジェクトライフタイムとゼロの一時停止を提供しますが、開発者はretain cycleを理解し、weak/unownedを正しく選択する必要があります。GCは開発者をこれらの懸念から解放しますが、非決定論的な一時停止と追加スレッドのコストがかかります。

Strong、Weak、Unowned:ARCにおける参照修飾子

ARCは3種類の参照修飾子を定義しており、それぞれがカウンタとオブジェクトのライフサイクルに異なる影響を与えます。正しい修飾子を選ぶことは、Swiftにおける安全なメモリ管理の基礎です。

Strong

Strongはデフォルトの修飾子です。各strong参照はオブジェクトのretain countを1増やします。少なくとも1つのstrong参照が存在する限り、オブジェクトは生き続けます。Swiftのすべてのクラスプロパティとローカル変数はデフォルトでstrongです。strong参照は所有関係を作成します:オブジェクトAはオブジェクトBを所有します。

Weak

Weakはretain countを増やさない参照です。weak参照がオブジェクトを指していても、オブジェクトは解放される可能性があります。解放後、weak参照は自動的にnilに設定されます。weak参照は常にvarとしてオプショナル型(?)で宣言されます。特にdelegateパターンで、retain cycleを断ち切るために使用されます。

Unowned

Unownedは非所有参照であり、weakと同様にretain countを増やしません。ただし、unowned参照は解放後にnilに設定されません。解放されたオブジェクトにアクセスするとクラッシュが発生します。Unownedは、オブジェクトが参照元のオブジェクトと少なくとも同じ期間生存することが保証されている場合に使用されます。典型的な使用例はクロージャ(closures)と、生存期間が保証された親子関係です。

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) deallocated") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — 所有しない
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) deallocated") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// CustomerとCreditCardは両方とも解放 — retain cycleなし

ここでCreditCardはCustomerへのunowned参照を使用しています。Customerはカードを所有し(strong)、カードは顧客を所有しません(unowned)。Customerが解放されると、両方のオブジェクトが解放されます。retain cycleは発生しません。card.customerがstrongだった場合、サイクルが解放をブロックします。

ARCの一般的な問題とその解決策

自動化にもかかわらず、ARCは万能薬ではありません。開発者は、内部のメモリ管理メカニズムの理解を必要とするいくつかの典型的な問題に直面します。

クロージャでのRetain Cycle

Swiftのクロージャ(closures)は、外部変数をstrong参照でキャプチャします。クロージャがクラスプロパティに割り当てられ、selfをキャプチャすると、retain cycleが発生します:クラスがクロージャを保持し、クロージャがselfを保持します。解決策は、weakまたはunownedを使用したキャプチャリストです。

swift
class NetworkManager {
    var completionHandler: ((Data?) -> Void)?
    var data: Data?

    func fetchData() {
        completionHandler = { [weak self] result in
            guard let self else { return }
            self.data = result
            self.processResult()
        }
    }

    func processResult() { }
}

キャプチャリスト[weak self]は、クロージャ内でselfへのweak参照を作成します。これにより、潜在的なretain cycleが断ち切られます。Guard let selfは、コードを実行する前にオブジェクトが生きていることを保証します。weak selfは、Swiftにおける非同期クロージャの標準的なプラクティスです。

Retain/Releaseのパフォーマンス

retain/releaseは軽量な操作ですが、ホットループでの頻繁なカウンタの増減はオーバーヘッドを追加します。Swift 5.9+では、アナライザが安全であることを証明した場合、コンパイラは冗長なretain/releaseを削除する最適化を使用します。ただし、Objective-Cでは、毎秒数百万回の呼び出しがある高負荷シナリオで、retain/releaseが依然としてボトルネックになる可能性があります。

Autorelease Pool

Autorelease Poolは、Objective-Cおよび一部のSwiftシナリオで使用される遅延解放メカニズムです。オブジェクトはプールに配置され、プールが drained になるとreleaseを受け取ります。多くの一時オブジェクト(JSON解析など)を含むループでは、カスタムautoreleasepoolを作成することで、ピークメモリ消費を削減できます。

よくある質問

ARCは手動メモリ管理(MRR)とどう違うのですか?

手動管理(MRR)では、開発者は明示的にretain、release、autoreleaseを呼び出していました。ARCはこれらの呼び出しをコンパイル時に自動的に挿入し、double-freeのリスク、release忘れによるリーク、retain/releaseのバランスエラーを排除します。

ARCはC/C++コードで動作しますか?

ARCはObjective-CオブジェクトとSwiftクラスのみを管理します。C/C++の構造体やポインタにはARCは適用されません。これらのオブジェクトは手動で、またはC++のスマートポインタ(shared_ptr、unique_ptr)を介して管理されます。Core Foundationオブジェクト(CFString、CGColor)もARCの対象外です。

weakとunownedはどのように使い分けますか?

weak — オブジェクトが参照元より先に解放される可能性がある場合(デリゲート、非同期クロージャ)。unowned — オブジェクトが少なくとも参照元と同じ期間生存することが保証されている場合(子が親なしでは存在できない親子関係)。確信がない場合はweakを選択してください。

Existential Typeとは何ですか?ARCにどのように影響しますか?

SwiftのExistential Type(プロトコルを型として使用)は、値を特別なコンテナ(existential container)にラップします。これにより、プロトコル境界でのretain/releaseの数が増加します。Swift 5.7+では、opaque result typeとsomeパラメータがコンテナを排除することでオーバーヘッドを削減します。

Swiftでretain countを確認するには?

Swiftでretain countを読み取るための直接的なAPIはありません。実装の詳細とみなされています。診断には、XcodeのInstruments(Allocations、Leaks)またはメモリデバッガを使用してください。これらのツールは、ライブクラスインスタンスの数と保持チェーンを表示します。

まとめ

  • ARC — SwiftおよびObjective-Cのためのコンパイラベースのメモリ管理システム。参照カウントによって動作する
  • 原理 — 各オブジェクトにはretain countがあり、ゼロになると即座に決定論的に解放される
  • GCとの違い — ARCはバックグラウンドスレッドやStop-The-Worldの一時停止なしで動作するが、retain cycleの制御が必要
  • Strong — カウンタを増やす。weakとunownedは増やさないが、unownedは解放時にnilにならない
  • クロージャ — Swiftにおけるretain cycleの主な原因。[weak self]キャプチャリストが標準的な解決策
  • Autorelease Pool — ループやカスタムシナリオにおける一時オブジェクトのための遅延解放メカニズム
  • 診断 — Xcodeメモリデバッガ、Instruments、LeakCanary(ObjCブリッジ経由)で問題を発見

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

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

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

こちらもお読みください