krsakai / iOSStudy

1 stars 0 forks source link

循環参照について #28

Closed krsakai closed 7 years ago

krsakai commented 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]のオプショナルで書いている所が多いという経緯がある

2017-05-03 3 09 42