0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RetainCycle(循環参照)とweak/unowned【iOSメモリ管理の基本vol.2】

Posted at

はじめに

Swiftでは、オブジェクト同士が互いに参照し合うことでメモリリークが発生することがあります。この問題は「Retain Cycle(循環参照)」と呼ばれ、アプリのクラッシュやメモリ使用量の増加を引き起こす原因になります。
本記事では、Retain Cycleの仕組みとその対策として使われる weak, unowned について、コード例を交えながら解説します。

本記事に登場するARC(Automaric Reference Counting)についてはこちらの記事で解説しています。

強参照 (strong) とRetain Cycle

Swiftではメモリを参照する方法が2つあり、その1つが強参照 (strong)です。

強参照の特徴

  1. 他のオブジェクトが強参照している限り、メモリは解放されない
  2. Swiftのデフォルトの参照方法

この強参照とARCが組み合わさることで起こりうるのがRetain Cycle (循環参照) です。

Retain Cycleとは

  • 2つのオブジェクトが お互いを強参照 していると、どちらの参照カウントも0にならず、メモリが解放されない=メモリリークになる現象

以下の例をみてみましょう。

class Person {
    var name: String
    var pet: Cat?

    deinit {
        print("\(name)は解放されました!")
    }
}

class Cat {
    var name: String
    var owner: Person?

    deinit {
        print("猫の\(name)は解放されました!")
    }
}

var jiro = Person(name: "じろう")
var meow = Cat(name: "ミャオ")

jiro.pet = mewo
meow.owner = jiro

jiro = nil // ⚠️ 何も出力されない

上はjiro: Personmeow: Catpet: Cat?として持っているといった例です。
deinitはオブジェクトが参照されなくなったタイミングで呼び出されます。

最後の行でjiro = nilとしているのにdeinitは呼ばれていません。これは、jiroがmeowから強参照されているためです。

weakとunowned

weakunownedはオブジェクトの参照方法を明示的に指定することができるSwiftのキーワードです。主に以下の特徴があります。

キーワード ARCカウント いつ解放される? Optional
weak されない 参照先が解放されると解放 Yes
unowned されない 参照先が解放されたらクラッシュする No

weakを使って参照している場合、参照先のメモリが解放されると、自動的に弱参照が消えます。そのため、weakを使うことでRetain Cycleを防ぐことができます。
weakを用いて上記のコードを修正してみます。

class Person {
    var name: String
    var pet: Cat?

    deinit {
        print("\(name)は解放されました!")
    }
}

class Cat {
    var name: String
    weak var owner: Person? // // 循環参照を防ぐためweakを追加

    deinit {
        print("猫の\(name)は解放されました!")
    }
}

var jiro = Person(name: "じろう")
var meow = Cat(name: "ミャオ")

jiro.pet = meow
meow.owner = jiro

jiro = nil  // 🔄 このとき Pet → Cat の strong参照がなくなり、Personも解放される
            // ⭕️ "じろうは解放されました!"
            
meow = nil  // 🔄 これで Cat も解放される
            // ⭕️ "ミャオは解放されました!"

一方でunownedを使って参照している場合、もし参照先のオブジェクトが先に解放されると、アプリがクラッシュしてしまいます。
unownedを使うべきなのは、「参照先が自分よりも先に解放されない」ことが保証されているときです。
例えば、アプリ起動中ずっと存在し続けるような RootView のようなオブジェクトに対しては、unownedでの参照が適しているケースがあります。

まとめ

  • Swiftではデフォルトで「強参照(strong)」が使われ、これがRetain Cycleの原因になることがあります

  • Retain Cycleとは、2つのオブジェクトが互いに強参照し合うことでメモリが解放されなくなる現象です

  • weak は参照先が解放されると自動で nil になり、循環参照を防げます(Optional型)

  • unowned は非Optionalで使える反面、参照先が先に解放されるとクラッシュするため、使い所に注意が必要です

  • Retain Cycleのリスクがある場面では、weak もしくは unowned を適切に使うことで安全なメモリ管理が可能になります

さいごに

お読みいただきありがとうございました!
この記事がSwiftでのメモリ管理やRetain Cycleの理解に少しでも役立てば嬉しいです。
私自身もまだ勉強中の身ですので、もし内容に誤りや不明点があれば、ぜひご指摘いただけると助かります。
一緒に成長していきましょう!💪

参照

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?