🕊
[Swift] weak self しないと強参照で解放できないサンプルコード
はじめに
weak self
しないことによって、参照元が強参照のため、解放できないサンプルコードを作るのが難しかったので、メモの代わりにここに残します。
適当な closure を持つ struct を用意
struct Calculator {
let numA: Double
let numB: Double
let closure: (Double, Double) -> Void
init(numA: Double, numB: Double, closure: @escaping (Double, Double) -> Void) {
self.numA = numA
self.numB = numB
self.closure = closure
}
func calc() {
closure(numA, numB)
}
}
強参照サンプルコード
class SampleStrongClass {
private let taxRate = 1.1
let numA: Double
let numB: Double
init(numA: Double, numB: Double) {
self.numA = numA
self.numB = numB
}
deinit {
print("deinit: \(type(of: self))")
}
// 強参照なコード
func getCalculator() -> Calculator {
Calculator(numA: numA, numB: numB) { numA, numB in // ← `[weak self]` がないため強参照
print("\((numA + numB) * (self.taxRate))")
}
}
}
弱参照サンプルコード
class SampleWeakClass {
private let taxRate = 1.1
let numA: Double
let numB: Double
init(numA: Double, numB: Double) {
self.numA = numA
self.numB = numB
}
deinit {
print("deinit: \(type(of: self))")
}
// 弱参照なコード
func getCalculator() -> Calculator {
Calculator(numA: numA, numB: numB) { [weak self] numA, numB in // ← `[weak self]` をつける
guard let self = self else {
print("self is nil")
return
}
print("\((numA + numB) * (self.taxRate))")
}
}
}
強参照と弱参照の比較
強参照の場合
// 初期化
var smapleStrongClass: SampleStrongClass? = .init(numA: 3, numB: 4)
var strongClassCalculator: Calculator? = smapleStrongClass?.getCalculator()
// クロージャーの実行
strongClassCalculator?.calc() // 7.700000000000001
// 参照元の class に nil を代入
smapleStrongClass = nil // (出力なし) ← deinit が走らない = メモリリーク
// クロージャーの実行
strongClassCalculator?.calc() // 7.700000000000001 ← 計算できてしまう
弱参照の場合
// 初期化
var smapleWeakClass: SampleWeakClass? = .init(numA: 5, numB: 6)
var weakClassCalculator: Calculator? = smapleWeakClass?.getCalculator()
// クロージャーの実行
weakClassCalculator?.calc() // 12.100000000000001
// 参照元の class に nil を代入
smapleWeakClass = nil // deinit: SampleWeakClass ← deinit が走る
// クロージャーの実行
weakClassCalculator?.calc() // self is nil ← 計算できない
以上になります。
Discussion