Swiftではクロージャー内でselfを使う時には[unowned self]を使わないとメモリリークが発生する事があります。
method({ [unowned self]
self.hoge()
})
メモリリークが発生する状況
下のようにclosureからself, selfからclosureを参照している場合はお互いに参照しあってしまいメモリリークが発生します。
class MyClass {
var closure: (() -> ())? = nil
func method() {
closure = {
print(self)
}
}
}
MyClass().method() // MyClassインスタンスは開放されない
メモリリーク対策
[unowned self]を入れれば循環参照を起こさず、メモリリークは発生しなくなります。
今回はこの[unowned self]の[]とは何かを書いていきます。
class MyClass {
var closure: (() -> ())? = nil
func method() {
closure = { [unowned self] in // 循環参照にならない
print(self)
}
}
}
MyClass().method()
[]とは何か
[]はSwiftのキャプチャと呼ばれる機能です。
selfに使われる事が多いですが、実は普通の変数にも使う事ができます。
キャプチャ機能について
[]で変数を囲むと、その後その変数に何を代入しても代入前の値を見るようになります。
var x = 0
var y = 0
let closure = { [x] in
print(x, y) // → 0, 10
}
x = 10
y = 10
closure()
オブジェクトのプロパティーを変更した場合は変更が反映されます。
let x = MyClass()
let y = MyClass()
let closure = { [x] in
print(x.value, y.value) // → 10, 10
}
x.value = 10
y.value = 10
closure()
配列のappendの場合は変更前の値を保持します。
var x = [1, 2]
var y = [1, 2]
let closure = { [x] in // → [1, 2], [1, 2, 3]
print(x, y)
}
x.append(3)
y.append(3)
closure()
下のように代入のような形で使う事もできます。
var x = 0
var y = 0
let closure = { [z = x] in
print(x, y, z) // → 10, 10, 0
}
x = 10
y = 10
closure()
[unowned self]とは何か
[unowned self]とは、非所有参照でselfをキャプチャする事です。
クロージャー内では元のselfでなく、新しい非所有参照のselfを使うので循環参照が起こらなくなります。
let closure1 = { [unowned self] in
print(self)
}
// ↑ と ↓ は同じ意味
let closure2 = { [unowned this = self] in
print(this)
}