【iOS】「SwiftにおけるARC 基本とその先」の循環参照と弱参照について解説

今回はいまさらながらプログラミング、アプリ開発の基本であるメモリの管理についてSwiftではどのようになっているのかApple Developerの「SwiftにおけるARC:基礎とその先」の内容で理解が難しいと感じた箇所について少し詳しくまとめましたので解説していきたいと思います。
Swift ARC
Swiftにおけるオブジェクトのメモリ管理機能であるARC(Automatic Reference Counting)について、Apple Developerの「SwiftにおけるARC:基礎とその先」を参照したのですが、循環参照や、循環参照の回避策について少し理解が難しいと感じたのでその辺りに重点を置いて解説していきたいと思います。
Swiftのオブジェクトのメモリ管理はARC(Automatic Reference Counting)により行われます。これは参照している箇所をコンパイラが自動的にカウントしカウントが0になったら不要と判断し自動解放する仕組みです。基本的には不要になったオブジェクトの解放処理は不要なのですが、注意する点は、循環参照を行うとそのオブジェクトは解放されずに残ってしまうことです。
ARCはコンパイラが不要となったオブジェクトを自動解放処理しますが、解放するタイミングはコンパイラにより決まるため、テスト段階では正常に動作することもあり潜在的なバグとなります。そのため、必ずきちんと理解しておくことが大切と説明されています。
循環参照
循環参照とは
2つ以上オブジェクトが互いを参照し合い、閉じたループを形成している状態です。A→B→Aや、A→B→C→Aのように、参照の連鎖が元の場所に戻ってくることで循環が発生します。これはメモリリークの問題を引き起こす可能性があります。

Swiftコードにおける循環参照例
ObjectAはObjectBを保持し、ObjectBはObjectAを保持するというようにお互いのクラスを参照し合っています。これを循環参照といいます。
class ObjectA {
var objectB: ObjectB?
}
class ObjectB {
var objectA: ObjectA?
init(_ objectA: ObjectA?=nil) {
self.objectA = objectA
}
}
func test() {
let objectA = ObjectA()
let objectB = ObjectB(objectA)
objectA.objectB = objectB
}
循環参照になると、ObjectAを使用する箇所がなくなってもObjectBが参照しているためARCによるカウントは0になりません。同様にObjectBもObjectAが参照しているためARCによる自動解放の条件が成立せず解放されないオブジェクトができてしまいます。これら解放されないオブジェクトが蓄積されるとアプリの動作が重くなる、アプリがクラッシュするなどさまざまな不具合が発生します。
これらを解決する方法として弱参照(ARCがカウント対象にしない)宣言がよく紹介されています。
循環参照を弱参照(weak)で回避
弱参照(weak)宣言する(緊急対策としては有りだと思うけどおすすめしない)
先ほどのObjectBで保持しているObjectAを弱参照として宣言することによりARCによるカウントはされず循環参照は解消されて使用しなくなった後、OSにより解放されるようになります。
class ObjectB {
weak var objectA: ObjectA?
init(_ objectA: ObjectA?=nil) {
self.objectA = objectA
}
}
ここで注意しなくてはいけないのはObjectBが保持しているObjectAの値の解放タイミングはコンパイラにより決まるため、ObjectBで参照して処理する場合に注意が必要です。
weakによる回避をおすすめしない理由
- 予期せぬ不具合なる可能性がある。
・弱参照オブジェクトが解放された後に、弱参照オブジェクトを強制アンラッピング(!)して使用するているとクラッシュする
・弱参照オブジェクトが解放された後、処理中に参照できなくなるのでなどのデータに不整合が生じる - コード改修のコストが増加する
・機能追加、機能変更、他の不具合修正などによるコード改修の際に弱参照変数の有効期間を注意した変更が必要になる。
・改修時の考慮不足による新たな不具合を生む可能性が増す。
・改修時にそれなりに注意する点が増えることで検証コストが増える。
循環参照と弱参照まとめ
極力弱参照(weak)を使用しないよう設計時に心がけることが重要。
※基本的に、弱参照は初期段階では取り入れないことが望ましいと考える
開発終盤など日程的に設計見直しのための工数確保が厳しいなどの場合における一時的対応手段として考えた方が良いでしょう。
次回は、循環参照回を回避するベストな方法に次回、Apple Developerの「SwiftにおけるARC:基礎とその先」で紹介されている以下の内容について解説したいと思います。
- deinitを使用したケースの紹介と懸念点
- deinitの懸念点も解消した良いパターン
参考:WWWDC2021 - SwiftにおけるARC: 基礎とその先
