SwiftUIのViewはなぜクロージャでselfを書いても循環参照しないのか

SwiftUIでコードを書いていると、クロージャでViewの`self`を頻繁に使うと思います。大抵のiOSアプリ開発者は`self`を書かなければいけない場面にのみ`self`を利用しているので、循環参照しないように`weak`もしくは`unowned`にすることを考慮したくなりますが、SwiftUIのViewはそこは考慮しないでも良いようです。この記事はその理由などを書いています。

# なぜSwiftUIで`self`が登場しても循環参照しないのか

本題です。SwiftUIで`self`を頻繁に使う例として、Buttonのactionクロージャで`self`を使う例を利用します。

画面としては次のようなボタンだけのContentViewを考えます。

<img width="384" alt="スクリーンショット 2020-04-19 20.46.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/acb09e68-05e3-53db-9c41-64b3bf125533.png">

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button(action: { /* [weak self] にそもそもできない */
            self.foo()
        }) {
            Text("ここを押しなさい...")
        }
    }

    func foo() {
        print("foo")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

このようになぜ`self`をそのまま使っても循環参照しないのかについて箇条書きにします。

- ContentViewはButtonの**参照を保持していない**
    - ButtonはViewのComputed Property `var body: Self.Body { get }` で呼び出されているだけ
- Buttonはactionクロージャをescapingはしている
    - https://developer.apple.com/documentation/swiftui/button/3335832-init
- escapingしているがactionクロージャはContentViewの**値をキャプチャしている（値がコピーされている)だけ**
    - そもそも`[weak self]`にできない
    - 構造体なので値がコピーされている
        - おそらくアドレスを調べると別物となっているはず
            - 基本的には構造体のメンバを変更することも出来ない（mutateにできない）

つまり参照をしていないわけです。これを次のように図で概略を示します。

<img width="1045" alt="PNGイメージ.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16400/e01dc394-49ce-5767-aab1-e9ed0b26b102.png">


# `@ObservedObject`や`@State`について

## Viewの構造体が保持する`@ObservedObject`や`@State`は変更できる件

こっから話変わります。循環参照の話は終わりです。

「基本的には構造体のメンバを変更することも出来ない」というのを上で書きましたが、Buttonのactionクロージャで`@ObservedObject`や`@State`は変更できるはずです。

```swift
import SwiftUI

struct ContentView: View {
    @State var state = false

    var body: some View {
        Button(action: {
            self.state.toggle()
            self.foo()
        }) {
            Text("ここを押しなさい...")
        }
    }

    func foo() {
        print("foo \(state)")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

ボタンを押すと次のように`state`が変化します。

```
foo true
foo false
```

## Viewの構造体が保持する`@ObservedObject`や`@State`が更新されても値が変わらない件

Viewが更新される際にstructのSubViewは作り直されています。次の記事が参考になるでしょう。

「SwiftUIのSubViewは画面更新ごとに生成と破壊を繰り返す」
https://qiita.com/yimajo/items/a0180e66f4f93120287e


これについては`@ObservedObject`や`@State`は構造体のメンバとなっていますが、これらは「別の領域に保持されている」か、もしくは「Viewの更新時にその値がうまいこと引き継がれる」のでしょう。

ここでリファレンスを読むと、`@ObservedObject`や`@State`はプロトコルDynamicPropertyに準拠しており、[DynamicProperty](https://developer.apple.com/documentation/swiftui/dynamicproperty)の[`update`メソッド](https://developer.apple.com/documentation/swiftui/dynamicproperty/3363728-update)はbodyの再計算時、つまりViewの更新前に値を再セットするような気がします。


> Updates the underlying value of the stored value.


SwiftUIフレームワーク側の定義を読むとそんな感じの事も書いています

```swift
/// Represents a stored variable in a `View` type that is dynamically
/// updated from some external property of the view. These variables
/// will be given valid values immediately before `body()` is called.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol DynamicProperty {

    /// Called immediately before the view's body() function is
    /// executed, after updating the values of any dynamic properties
    /// stored in `self`.
    mutating func update()
}
```

`@ObservedObject`や`@State`などの変数は、Viewが更新される`body()`呼び出しのタイミングでupdateメソッドが動作し、その際に値が再セットされることでうまいこと引き継がれるんでしょう。


# まとめ

- SwiftUIのViewは構造体
    - （ここに書いてなかったけどクラスにするとコンパイルエラー）
    - クロージャにキャプチャするとコピーされているのでそもそも参照されていない
        - View構造体メンバの`@ObservedObject`や`@State`やらの値はどうやって元のままなの？
            - `DynamicProperty`の仕組みによってうまいこと引き継がれている
                - 1描画ごとにView構造体は都度作り直されることもあるし、そうでないこともある