8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SwiftAdvent Calendar 2023

Day 11

図で理解する循環参照〜視覚的に循環参照を理解しよう!〜

Last updated at Posted at 2023-12-10

循環参照とは

2つのインスタンス同士がお互いを参照しあっている関係を循環参照と呼びます。インスタンス同士が循環参照を起こしていると、メモリを解放することができません。なぜなら、お互いを参照しあっているので参照カウンタが0にならず、ずっとを保持し続けてしまうからです。
この問題をメモリリークと言い、メモリ領域の圧迫によってパフォーマンスの低下や、場合によってはアプリケーションを終了させてしまいます。

循環参照の例

ViewController.swift
import UIKit
class ViewController: UIViewController {
    var closure: () -> Void = {}
    var value = 100
    
    override func viewDidLoad() {
        super.viewDidLoad()
        closure = { print(self.value)}
    }
}

ViewControllerがclosureを参照しており、closureがViewControllerのvalue変数を参照しているので循環参照が起こっています!

参考文献:selfが何が指しているかわからない場合は下記の記事を参考にしてみてください!

XcodeのDebug Memory Graphで参照関係を視覚的に確認してみましょう!

下記の画像からもわかるように、ViewControllerがclosure変数を参照しており、closure変数がViewControllerを参照している。

このように2つのインスタンス同士がお互いを参照しあっているので、循環参照が発生している!

参考文献:XcodeのDebug Memory Graphの表示方法は下記の記事に載っています!

循環参照の解決

参照型は3つの参照方法がある!

1.強参照

デフォルトはstrong

2.弱参照

weak
参照先が存在しない場合に参照しようとされると自動でnilになってる

3.非所有参照

unowned
参照先が存在しない場合に参照しようとされると解放済みへのアクセスとなりクラッシュする

循環参照をするためにweakとunonwnedを使うと強参照することなく、他のインスタンスを参照することができます。
強参照されないと参照カウンタからカウントされません。そのため、インスタンスは循環参照をせずにお互いを参照することができます。

今回はweakによる循環参照の解決方法を詳しく見ていきます!

弱参照weakによる循環参照の解決

ViewController.swift
import UIKit

class ViewController: UIViewController {
    var closure: () -> Void = {}
    var value = 100
    override func viewDidLoad() {
        super.viewDidLoad()
        closure = { [weak self] in
            if let strongSelf = self {
                print(strongSelf.value)
            }
        }
    }
}

[weak self]で弱参照にすることで、ViewController画面がないかもしれない場合を扱うことができる!つまり、画面が開放されていなければ、selfにViewControllerが入っている。
画面が解放されていれば、参照先が存在しないのに参照しようとされるので自動でnilになってくれる。

上記のコードのclosureスコープ内を詳しく見てみましょう!

ViewController.swift
        closure = { [weak self] in
            if let strongSelf = self {
                print(strongSelf.value)
            }
        }

このclosureが呼ばれた時にViewControllerの画面がない可能性があるので、if文を用いてself(ViewControllerのインスタンス)があるならば、value変数の値をプリントできます!
[weak self]で弱参照にすることで、if文内のselfも弱参照になるため、循環参照を解決できます。

XcodeのDebug Memory Graphで確認してみても循環参照になっていません。

closureからViewControllerへの参照が弱参照になっています!

closureからViewControllerを参照する際、参照先が存在しない場合に参照しようとされると自動でnilになってくれるので循環参照を防ぐことができます!

参考文献

8
5
1

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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?