Swiftでクロージャが入れ子の場合のキャプチャリスト
はじめに
Swiftのクロージャには循環参照を回避するための仕組みとしてキャプチャリストがあります。
最近、キャプチャリストがあるのにメモリリークするというバグに遭遇して、自分の認識が間違っていたことが分かったのでメモしておきます。
結論としては、クロージャが入れ子の場合には、直接使っていなくても外側にキャプチャリストを書いておかないと強参照になります。
バージョン
Swift 4.0
サンプルコード
class Hoge { deinit { print("deinit") } let n: Int init(n: Int) { self.n = n } // 内側だけweak lazy var closure1: () -> Int = { return {[weak self] in return (self?.n)! * 2}() } // 外側だけweak lazy var closure2: () -> Int = { [weak self] in return {return (self?.n)! * 2}() } // 両方weak lazy var closure3: () -> Int = { [weak self] in return {[weak self] in return (self?.n)! * 2}() } // 内側だけunowned lazy var closure4: () -> Int = { return {[unowned self] in return self.n * 2}() } // 外側だけunowned lazy var closure5: () -> Int = { [unowned self] in return {return self.n * 2}() } // 両方unowned lazy var closure6: () -> Int = { [unowned self] in return {[unowned self] in return self.n * 2}() } } do { // deinit呼ばれない print("closure1") let hoge = Hoge(n: 100) print(hoge.closure1()) } do { print("closure2") let hoge = Hoge(n: 100) print(hoge.closure2()) } do { print("closure3") let hoge = Hoge(n: 100) print(hoge.closure3()) } do { // deinit呼ばれない print("closure4") let hoge = Hoge(n: 100) print(hoge.closure4()) } do { print("closure5") let hoge = Hoge(n: 100) print(hoge.closure5()) } do { print("closure6") let hoge = Hoge(n: 100) print(hoge.closure6()) }