Closed krsakai closed 7 years ago
循環参照はインスタンス①とインスタンス②でお互いの強い参照を持つことで、インスタンス①②を解放した後もインスタンスが残ってメモリ上に残り続けしまい、それが悪化するとメモリリークを起こす問題のこと
class Test1 { var test2: Test2! deinit { // ← インスタンスが解放されたら呼ばれる print("Test1が開放されました") } } class Test2 { var test1: Test1! deinit { print("Test2が開放されました") } }
var test1 = Test1() var test2 = Test2() test1?.test2 = test2 test2?.test1 = test1
↑この状況下で↓を呼んでも deinitが呼ばれず、迷子インスタンスとしてメモリに残ってしまう (deinitはインスタンスが開放時に必ず呼ばれるメソッド)
test1 = nil test2 = nil
単に↓の状況下なら 循環参照前なのでdeinitが呼ばれて解放される
var test1 = Test1() var test2 = Test2() test1 = nil test2 = nil
なので、メモリを切迫しないように循環参照はやめましょうというお話 。結構こういう話は出てくるので事象だけでも覚えておくように
また、クロージャでも似たような事が起こるので確認しておくと、
class ViewController: UIViewController { var closure: (() -> Void)? init() { self.closure = { self.dismiss(animated:true completion:nil) } } deinit() { print("ViewControllerが開放されました") } }
↑ こうゆうような状態の時に、↓を実行してもdeinitが呼ばれれない
let viewController = ViewController() // ← (補足) コンストラクター/イニシャライザー これでinitが呼ばれる viewController = nil
なので、弱参照となるように↓のように書いている
self.closure = { [weak self] in self?.dismiss(animated:true completion:nil) }
self.closure = { [unowned self] in self.dismiss(animated:true completion:nil) } ↑と↓は同意味 self.closure = { _ in self.dismiss(animated:true completion:nil) }
ただ、この時に↓クロージャを別画面へ受け渡し、自身は解放するなどしてselfがいなくなった後に、別画面の方でこのクロージャーを実行すると、self がいないのでクラッシュする。そのため安全な [weak self]のオプショナルで書いている所が多いという経緯がある
self
[weak self]
循環参照について
循環参照はインスタンス①とインスタンス②でお互いの強い参照を持つことで、インスタンス①②を解放した後もインスタンスが残ってメモリ上に残り続けしまい、それが悪化するとメモリリークを起こす問題のこと
例
↑この状況下で↓を呼んでも deinitが呼ばれず、迷子インスタンスとしてメモリに残ってしまう (deinitはインスタンスが開放時に必ず呼ばれるメソッド)
単に↓の状況下なら 循環参照前なのでdeinitが呼ばれて解放される
なので、メモリを切迫しないように循環参照はやめましょうというお話 。結構こういう話は出てくるので事象だけでも覚えておくように
また、クロージャでも似たような事が起こるので確認しておくと、
↑ こうゆうような状態の時に、↓を実行してもdeinitが呼ばれれない
なので、弱参照となるように↓のように書いている
ただ、この時に↓クロージャを別画面へ受け渡し、自身は解放するなどして
self
がいなくなった後に、別画面の方でこのクロージャーを実行すると、self がいないのでクラッシュする。そのため安全な[weak self]
のオプショナルで書いている所が多いという経緯がある