ProgrammingiOS開発者

Swiftにおける所有権の自動転送(Automatic Reference Counting, ARC)のメカニズムについて説明し、それがメモリ管理にどのように関連しているか。

Hintsage AIアシスタントで面接を突破

答え。

問題の歴史:

Objective-Cの進化とSwiftの登場に伴い、Appleではプログラマーがretain/releaseコマンドに明示的に関与することなく、アプリケーションのメモリを自動かつ安全に管理する方法が求められました。Swiftは、クラスオブジェクトへの参照の数を追跡し、最後の所有者が消えると自動的にメモリを解放するAutomatic Reference Counting(ARC)を使用しています。

問題:

Swiftはマルチパラダイム言語であり、値型(struct, enum)と参照型(class)を操作します。特に参照型においては、メモリを適切なタイミングで解放することが重要であり、そうでなければメモリリークが発生する可能性があります。retain cycle(循環参照)が発生する場合、2つのオブジェクトが互いに参照し合い、ARCがメモリを解放できなくなるという厄介な状況になります。

解決策:

SwiftはARCをクラスオブジェクトのみに適用します。参照の数が0になると、メモリが解放されます。retain cycleの問題を解決するために、weak/unownedといったキーワードが使用されます。

コード例:

class Person { var name: String var pet: Pet? init(name: String) { self.name = name } deinit { print("\(name)が解放されます") } } class Pet { var owner: Person? init() {} deinit { print("ペットが解放されます") } } var tom: Person? = Person(name: "Tom") var cat: Pet? = Pet() tom!.pet = cat cat!.owner = tom tom = nil cat = nil // 両方のオブジェクトは解放されない、retain cycleが発生

主な特徴:

  • メモリ管理は開発者にとって透過的。
  • クラスのみがサポートされ、structやenumは参照されない。
  • retain cycleの解決にはweakとunownedが使用される。

ひっかけ質問。

値型にARCを適用できますか?

いいえ、ARCはクラスオブジェクトへの参照のみを追跡します。構造体とenumは値渡しされ、Swiftは自動的にメモリを解放します(通常はスコープから出るとき)。

相互に参照し合う2つのオブジェクトのプロパティにweak/unownedを使用しないとどうなりますか?

循環参照が発生し、ARCはそのオブジェクトに対するメモリを解放しなくなります。このようなコードはメモリリークを引き起こします — このような参照の片方には常にweak(またはunowned)を使用してください。

weakがあるのにunownedは何のために必要ですか?

unownedは、参照が他のオブジェクトのライフサイクル中は常に存在し、nilになることがない場合に必要です。weakはnil可能な参照、unownedはnilでない参照ですが、retainカウンタは増加しません。

コード例:

class A { var b: B? } class B { unowned var a: A // Bのライフサイクル中は常にnilではない }

一般的なエラーとアンチパターン

  • weak/unownedが必要な場所で強い参照を使用すること。
  • クラスやオブジェクトを含む配列/辞書を操作する際にARCを忘れること。
  • 値型のメモリを手動で解放しようとしたり、struct/enumにweakを使用すること。

実生活の例

ネガティブケース

2つのコントローラが互いに強い参照を持っている。一方はデリゲートプロパティを介して、もう一方は子コントローラの配列を介して。作成者はデリゲートにweakを使用しない。

利点: エラーはすぐには見えず、一見すべてが「機能する」。

欠点: アプリケーションの規模が増すにつれてメモリが解放されず、リークが発生;制限されたメモリを扱うとクラッシュの可能性がある。

ポジティブケース

デリゲートとイベントリスナーはweakとして設定され、コントローラの配列はコントローラが消えるにつれてクリーンアップされる。すべてがきちんと文書化されている。

利点: リークがなく、すべてのオブジェクトがタイムリーに解放され、パフォーマンスの低下がない。

欠点: デリゲートが予期せず失われる可能性がある(どこかで強い参照を忘れた場合);アプリケーションのアーキテクチャに注意が必要。