循環参照？強参照？なにそれ？

# この記事で得られる情報

- Swiftの強参照についてふわっと理解できる
- SwiftのARCについてもふわっと理解できる
- Swiftの循環参照についてもふわっと理解できる
- Swiftでメモリリークしないように実装できるようになる（可能性が上がる）

## 初めに

コードレビューいただいた時によく

「ここ循環参照してるから、片方弱参照にして」

なんて指摘をもらいます。

その度に「は？とりあえずweakつければいっか」ってやっていました。
しかし、これでは良くないと思い改めて循環参照について調べてきました。

## 強参照ってなに？

まずは強参照についてふわっと学びましょう。

強参照とは、これ使っているよっていうメモのようなものです。

例えば、部屋に箱が2つ置いてあり、箱Aには「箱B使用中」と書かれたメモが入っています。

このメモが参照と言われるもので、箱Aが箱Bを使っている（参照している）状態といえます。

通常「箱B使用中」と書かれたメモがあったら、それは強参照だと思ってもらって構いません。

「箱B使ってるから絶対に捨てるなよ」と強く言われているようなものですからね…

では実際にコードを見てみましょう。
```swift
class ClassA {
	var b: ClassB?
}

class ClassB {}

var boxA = ClassA()
var boxB = ClassB()

// 箱Aが箱Bを使っている（参照している）
boxA.b = boxB
```
これがSwiftにおける強参照となります。

## ARCってなに？

先ほど同様、箱Aと箱Bが部屋に置いてあります。

あの箱邪魔だなあ、と思っていた佐藤さんはどちらの箱も捨てようとしますが

箱Aには「箱B使用中」というメモがありました。なので、箱Bを捨てることはできません。

しかし、佐藤さんは閃きます。「箱Aを捨ててしまえば、箱B使用中というメモも捨てることができるから、その瞬間箱Bも使用中じゃなくなるよね」

ということで佐藤さんは、箱Aを捨てた後に箱Bを捨てました。

現実世界でこんなことをしたら佐藤さんは非難されますが、Swiftの世界では「不要なものを勝手に捨ててくれるいいやつ」です。この佐藤さんのような存在をSwiftではARCと言います。

※ふわっと理解なので、ARCについて詳しく知りたい方は公式ドキュメントをご覧ください

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/

## 循環参照ってなに？

循環参照とは、お互いがお互いを参照しあっていることを言います。

先ほどの例で例えると

箱Aと箱Bが部屋に置いてあります。

あの箱邪魔だなあ、と思っていた佐藤さんはどちらの箱も捨てようとしますが

箱Aには「箱B使用中」というメモがあり、箱Bには「箱A使用中」というメモがあります。

この場合、どちらの箱も使用中ということになり、箱を捨てることができません。

言い換えれば、お互いの箱がお互いの箱を使用（参照）しあっている状態で、捨てることができません。

これを循環参照と言います。

では実際にコードを見てみましょう。

```swift
class ClassA {
    var b: ClassB?
}

class ClassB {
    var a: ClassA?
}

var boxA: ClassA? = ClassA()
var boxB: ClassB? = ClassB()

// 箱Aが箱Bを使っている
boxA?.b = boxB
// 箱Bが箱Aを使っている
boxB?.a = boxA
```

この、お互いがお互いを使っている（正確には参照している）状態を循環参照と言います。

循環参照しているときは箱を捨てることができないので、部屋が片付けられない状態となります。

これをメモリリークと言います。

### ちょっと難しいけど大事な話

先ほどのコードで循環参照を説明しました。

では、以下のように箱を捨てるとどうなるでしょうか。

```swift
class ClassA {
    var b: ClassB?
}

class ClassB {
    var a: ClassA?
}

var boxA: ClassA? = ClassA()
var boxB: ClassB? = ClassB()

// 箱Aが箱Bを使っている
boxA?.b = boxB
// 箱Bが箱Aを使っている
boxB?.a = boxA

// 箱Aを捨てる
boxA = nil
// 箱Bを捨てる
boxB = nil
```

「箱Aも箱Bも捨てたから部屋が綺麗！」となりそうですよね。

しかし実際は違います。

「箱Aが使っている箱Bが、箱Aを使っているから綺麗にならない」というのが正解で、これこそが循環参照の正体です。

「？？？？？」

そうなるのも仕方ないです。これは私の説明が問題で、私は

「問題を身近なものに例えて説明する」

ことを大切にしているのですが、そうすると循環参照の説明が難しすぎるので、誤って認識されることを恐れずに説明させていただきました。

## メモリリークしないようにするには？

では部屋を綺麗にするにはどうすれば良いか、ですが

「箱Aが使っている箱Bが、箱Aを使っている」というのが原因なので「箱Aが使っている箱B」を捨てます。

同じように「箱Bが使っている箱A」も捨ててしまいましょう。

具体的には以下のようにします。

```swift
class ClassA {
    var b: ClassB?
}

class ClassB {
    var a: ClassA?
}

var boxA: ClassA? = ClassA()
var boxB: ClassB? = ClassB()

// 箱Aが箱Bを使っている
boxA?.b = boxB
// 箱Bが箱Aを使っている
boxB?.a = boxA

// 箱Aの箱Bを捨てる
boxA?.b = nil
// 箱Bの箱Aを捨てる
boxB?.a = nil
// 箱Aを捨てる
boxA = nil
// 箱Bを捨てる
boxB = nil
```

こうすることで循環参照することなく、部屋を綺麗にすることができます。

### 弱参照でもできるよ

メモリリークを回避する方法として弱参照も挙げられます。むしろこっちのやり方がメインかもしれません

ここでも2つの箱AとBを例に説明します。

箱Aには「箱B使っているけど、箱Aを使わなくなったら箱Bも捨てていいよ」と書いてあります。

箱Bには「箱A使用中」と書いてあります。

佐藤さんは「箱A捨てたら箱Bも捨てれるじゃん♪」と閃き、部屋を綺麗にすることができました。

これが弱参照です。

では、実際に弱参照を使用したコードを見てみましょう。

```swift
class ClassA {
    weak var b: ClassB?
}

class ClassB {
    var a: ClassA?
}

var boxA: ClassA? = ClassA()
var boxB: ClassB? = ClassB()

// 箱Aが箱Bを使っている
boxA?.b = boxB
// 箱Bが箱Aを使っている
boxB?.a = boxA

// 箱Aを捨てる
boxA = nil
// 箱Bを捨てる
boxB = nil
```

このようにすると、箱Aを捨てると同時に箱Bも捨てられる状態となり、循環参照を回避することができます。

以上、この記事ではSwiftの強参照、ARC、循環参照についてふわっと理解できる内容を提供しました。

また、Swiftでメモリリークを防ぐための具体的な実装方法についても学びました。