クロージャの[weak self]はどんなときに使うのか
クロージャの[weak self]はどんなときに使うのか
クロージャの記事を見ると、weak selfがよく使われていますね。
今回の記事で、[weak self]とはなんぞやから、何となくそうゆうことねくらいになってくれると嬉しいです。
循環参照を避けよう
事前知識として、循環参照が良くないということを知っておきましょう。
AオブジェクトがBオブジェクトを参照すると、Bの参照カウントが1になります。
BオブジェクトからAオブジェクトを参照すると、Aの参照カウントが1になります。
これはAとBの間で参照し合っているので、循環参照という現象が起きています。
SwiftはARCというシステムで、オブジェクトをメモリで記憶しています。参照カウントが1以上のオブジェクトを記憶し、参照カウントが0になるとオブジェクトがメモリから破棄されるという仕組みです。
先ほど説明した循環参照では、2つのオブジェクトが参照しあっているため、永遠にそれぞれのオブジェクトの参照カウントが1のままになってしまいます。すると、永遠にメモリから、オブジェクトが解放されなくなってしまい、メモリリークが発生します。そのたびにアプリが止まってしまっては、ユーザー体験が悪くなってしまいますね。
長くなりましたが、循環参照はメモリリークの原因になるので避けましょうということです。
[weak self]とは
さて、ここから本題です。
結論からいうと、[weak self]は、他オブジェクトからselfへの参照を弱参照にして、循環参照を避けるものです。
参照カウントは、弱参照をカウントしません。通常、AとBが参照し合っていると循環参照が起こります。しかし、AからB、またはBからAへの参照を弱参照にすることで、循環参照を避けることができます。
こちらはクロージャを使う一例です。
class Sample {
let api = API()
var model: Model?
init() {
// トレイリングクロージャという記述方法
// クロージャが最後の引数の時にのみ使える記述方法
// クロージャに代入
api.get { [weak self] result in
// 受け取った結果を使う
self.model = result
}
}
}
class API {
// 引数にクロージャ(completion)をとる
func get(completion: ((Model) -> Void)? = nil) {
// API通信
// デコードして結果を(クロージャ)completionに渡す
let result = Model()
completion?(result)
}
}
struct Model {}
上記したコードはAPI通信をしてModelを受け取る時に、クロージャを使う例を上げています。
APIクラスではAPI通信周りで使う関数を定義しており、それらを他のクラスで使いまわすという感じですね。
この場合、Sampleクラスが、completionクロージャを参照しており、completionクロージャがselfとしてSampleクラスを参照しています。
一見、循環参照が起きそうですが、[weak swlf]を加えているので、completionクロージャからself(Sampleクラス)への参照が弱参照となり、参照カウントされず、結果的に循環参照を避けられていますね。
このように、[weak self]をつけて循環参照を避けていくことでメモリリークを避けましょう。
最後に
私自身、[weak self]をつけ忘れることが多々あるので、記事にしてみました。
勘違いしている点があれば、教えてください!
Discussion