[weak self]の使いどころ swift/kotlin勉強会#2用

##自己紹介

iOSアプリエンジニア
swift歴約3年。kotlin歴約8ヶ月。
Oisixでは、主にiOSアプリ、サーバーAPIなどの開発を担当しています。


---

##概要

* 循環参照回避などでよく使われる。
* kotlin(というかJavaだと)WeakReferenceが近いかも思います。
* kotlinとswiftを比較した時に、[weak self]はswift特有だと思ったのと、使い所を自分でも再確認したかったため、今日発表してみようと思った


---
##循環参照とは

```swift
class Person  {
    var closure: (()->Void)?
    
    func someFunc() {
        self.someFuncAsync {
            print(self)//<ここで、closureがselfを強参照してしまい、解放されなくなる>
        }
    }
    
    private func someFuncAsync(_ closure: @escaping (()->Void)) {
        self.closure = closure
    }
    
    deinit {
        print("deinit") //解放されないので、ここがコールされない
    }
}

var person: Person? = Person()
person?.someFunc()
person = nil //<~~> の部分で、closureがpersonをstrongCaptureしているため、ここでnilにしても、解放されない。
PlaygroundPage.current.needsIndefiniteExecution = true // playground上での非同期処理をできるようにする( https://developer.apple.com/documentation/playgroundsupport/playgroundpage/1964501-needsindefiniteexecution )
```

---
##解決策
※他にも色々方法あり

```swift
class Person  {
    var closure: (()->Void)?
    
    func someFunc() {
        self.someFuncAsync {[weak self] in
            print(self)//selfが弱参照のため、ここがコールされる前にselfが解放されていたら、この時点でself == nil。[unowned self]でもよいがそれだと、self == nilだった時にクラッシュする
        }
    }
    
    private func someFuncAsync(_ closure: @escaping (()->Void)) {
        self.closure = closure //closureをメンバ変数で持たないでも回避可能
    }
    
    deinit {
        print("deinit") // person = nilで（どこにも参照されなくなるんので）コールされる
    }
}

var person: Person? = Person()
person?.someFunc()
person = nil
PlaygroundPage.current.needsIndefiniteExecution = true
```


---
これ以外にも、循環参照パターンはいろいろあります。
詳しくは下記の通り。

https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID52

---
##[weak self]について

* closureでよく使われる。
* [~~]Swiftのキャプチャリストという機能。
* self以外や[unowned self, weak delegate = self.delegate]という感じにも使えます

---
（中の実装が見れない）iOS標準SDKのclosureを持つメソッドでは[weak self]しないと、循環参照になるのか？

```swift

DispatchQueue.main.async {
    print(self)
}

UIView.animate(withDuration: 1) {
    print(self)
}

DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
    print(self)
}

URLSession.shared.dataTask(with: URL(string: "https://www.google.co.jp")!) { (data, res, err) in
    print(self)
}.resume()
```
---
ならなかった。少なくとも今あげたものは。。。
じゃあ、さっきのは[weak self]しなくてよいのか？

---
そういうわけでもない。
~~例えば、下記の様なパターンで、selfがViewControllerなどの場合に、60秒後はViewController.dismiss後だったら、BADACCESSなどの予期しない実行エラーが発生します。~~
例えば、下記の様なパターンでselfがViewControllerだった場合でDispatchQueue.main.asyncAfter()のクロージャ内で何かself(ViewController)に対して処理することを想定していたときに、それがViewController.dismiss後であったら、それは予期しない動作となることもあります。
※ @furuyan さんのご指摘により修正しました。ご指摘ありがとうございます！

```swift

DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
    print(self)
}

```

このようにDispatchQueue.main.asyncAfter()のクロージャ内ではselfを強参照してもメモリリークにはならない。でも、想定した動作と異なる動作となる場合があります。

---
##それを防止するよくあるパターン

```swift

DispatchQueue.main.asyncAfter(deadline: .now() + 60) { [weak self] in
    guard let `self` = self else { return }
    print(self)
}

```

---
##guardするときの変数名どうするかもいろいろある。

```swift

guard let self = self else { return }

guard let weakSelf = self else { return }

guard let strongSelf = self else { return } // Alamofire/Alamofire のパターン
```

---
##まとめ

* クロージャ内は常に `[weak self]` (や `[unowned self]` )である必要はない。
* `[weak self]` か `self` を使うか迷ったら、 `[weak self]` の方が安全。
* `[unowned self]` の利用は設計ルール次第。

---