循環参照とは
循環参照とは、2つのオブジェクトがお互いに強い参照を保持し合っている状態のことを指します。お互いにインスタンスを参照しあうため、どちらも解放されずにそのまま残り続けてしまうという現象です。
循環参照が発生してしまうと、メモリリークが発生し、プログラムのパフォーマンスが低下したり、クラッシュする原因となります。
発生例
例)AクラスとBクラスが存在し、それぞれ参照し合っている場合を考えてみましょう。
class A {
var b: B?
init() {
b = B()
b?.a = self
}
}
class B {
var a: A?
}
上記のコードを見ると、Aクラスのインスタンスが作成されると同時に、Bクラスのインスタンスも作成され、Aクラスのbプロパティに代入されているのが分かります。
また、Bクラスのインスタンスは、Aクラスのインスタンスを参照するように設定されます。
このように、AクラスとBクラスのインスタンスがお互いに強参照を保持し合っている場合に循環参照となりメモリリークが発生してしまいます。
強参照と弱参照
解説でも出てきた強参照というワードですが、それぞれ強参照(strong reference)と弱参照(weak reference) というものが存在します。
強参照とは、端的に言えば親子関係のようなもので、
親が解放されると子も自動的に解放されます。
上記のようにお互いが親としてインスタンスを保持すると循環参照が起きるわけです。
この循環参照を防ぐために弱参照というものがあります。
weakとunownedと言った装飾子を使用します
弱参照を使用することで、参照先のオブジェクトが解放された後も、参照が残っているオブジェクトに対して安全にアクセスできます。
弱参照のweakとunownedについて
weak
observable
.subscribe(onNext: { [weak self] string in
self.textLabel.text = string
})
weak selfの場合は、selfにnilが代入される可能性があるのでweakを使うときはOptionalでないといけません。
そのため、オプショナルバインディングでnilチェックをする必要がある為、コードがやや冗長になることがあります。
unowned
observable
.subscribe(onNext: { [unowned self] string in
self.textLabel.text = string
})
unowned selfは常に非nilであることが保証されています。
nilチェックが不要であるため、コードが簡潔になるメリットがありますが、非nilであることが保証されているため、nilが代入される可能性がある場合にunownedを参照すると落ちます。
weakとunownedの使い分け
nilの可能性がある場合には、weak selfを使用、
非nilの場合は、unowned selfを使用するという使い分けで良いと思います。
それぞれにメリット、デメリットはありますが、基本的にはweakを使っておけば問題になることはないと思います。
ただ、インスタンスが必ず存在すると保証できる時にweakを使うとアンラップが必要な分、記述が冗長になってしまいます。