LoginSignup
30
21

More than 5 years have passed since last update.

initとdeinitでメモリの動きを確認する

Posted at

意味のある検証かどうかは置いておいて…

私はメモリに対して
いらなくなったらきちんと解放してくれるのだろうか・・・
使いたい時に使えるようにきちんと持っているのだろうか・・・
という漠然とした2つの恐怖心を持ってます...

幸いメモリの確保と解放はinitdeinitにprint文を埋め込んで追うことができるようなので、幾つかのプログラムを検証して理解を深めていきたいと思います。

なお、deinit
変数にnilが代入されるもしくは
変数のスコープ外に処理が移動すると動き出すようなので
今回の動作検証ではnilを代入してみました。

1.最も単純なケース

class A {
    init(){
        print("A init")
    }
    deinit{
        print("A deinit")
    }       
}

var a:A? = A() // "A init"
a = nil        // "A deinit"

これは予想通り。まだまだ理解できる...!

2.他の変数が参照しているケース

class A {
    // 省略
}

var a:A? = A() // "A init"
var a2:A?

a2 = a         // a2もaを参照!!

a = nil        // 何も起きない...
a2 = nil       // "A deinit"

a = nilのところでA deinitと出ない!
これはARCのおかげで複数の変数から参照されている時は、メモリを解放せず残してくれるようです。

3.インスタンス変数に別クラスのインスタンスを持っているケース

class A {
    var b:B? = B()
    // 省略
}

class B {
    // 省略(initで"B init" deinitで"B deinit")
}

var a:A? = A() // "B init"
               // "A init"

a = nil        // "A deinit"
               // "B deinit"

initはB⇨Aの順に行われて、deinitは反対にA⇨Bの順に行われる模様。
入れ子になっている時は、上位のインスタンスを解放すれば、ぶら下がっているインスタンスも自動的に解放されていくらしい。

4.インスタンス変数を外から参照しているケース

class A {
    // 省略(3.のケースと同じ)
}

class B {
    // 省略(3.のケースと同じ)
}

var a:A? = A() // "B init"
               // "A init"

var b2:B?
b2 = a!.b      // b2にaのインスタンス変数bを参照させる

a = nil        // "A deinit"

b2 = nil       // "B deinit"

少し複雑になってきました。
3.の例でaの解放によって自動的に解放されたbは、今回外部のb2という変数から参照されているため残り続けています。

2.の例では複数の参照がある場合にメモリを残しておいてくれることにありがたみを感じていましたが、「上位のインスタンスをnilにすればメモリ解放は万事OK」なんて思ってると痛い目を見そうです。これは意外とくせ者かも...

5.インスタンス変数にDictionaryを使ってみたケース(なんとなく検証)


class A {
    var dict : Dictionary = Dictionary<String, AnyObject>()

    init(){
        print("A init")
        let b:B? = B()
        let c:C? = C()

        self.dict["b"] = b
        self.dict["c"] = c
    }

    deinit{
        print("A deinit")
    }
}

class B {
    // 省略(3.のケースと同じ)
}

class C {
    // 省略(initで"C init" deinitで"C deinit")
}

var a:A? = A() // "A init"
               // "B init"
               // "C init"

var b2:B?              
b2 = a!.dict["b"] as? B  // b2にaのインスタンス変数bを参照させる

a = nil        // "A deinit"
               // "C deinit"
b2 = nil       // "B deinit"

やっぱり外で参照しているインスタンスは残っていました。

他にも例が思いついたら書き足していこうかと思います。

30
21
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
21