社内のアプリ勉強会での資料で、メモリ管理の初歩的なところをキュッとまとめています。
メモリ管理の方法はリファレンスカウンター方式
リファレンスカウンター方式とは
インスタンスが何箇所から参照されているかのカウントを保存していて、
それが0になったタイミングでインスタンスをリリースする方式
昔は変数に代入するタイミングでretain(+1)やら
変数を使わなくなったタイミングでrelease(-1)と手動で操作していた。
その辺を自動でやってくれるARC(Automatic Reference Counting)なるものが出来たものの、どうやってARCが機能しているかは理解する必要がある。
勉強する順序としては、「リファレンスカウンターの仕組み」→「ARC」が良い
注意ポイント2つ
ARCの登場により、殆どメモリ管理については考える必要がなくなったが、次の2つは意識する必要がある。
・循環参照
・for文の中で大きいインスタンスを生成してる場合
循環参照
循環参照とは二つのインスタンスがお互いの参照を持っていてどっちもリリース出来無い状況。
親子関係にあるインスタンスで子供が親の参照を持ってしまうとこうなる。
自分でdelegateを実装するときに陥りがち。
これを回避する方法として、どちらか片方のインスタンスのプロパティをweak(リファレンスカウンタを増やさ無い)で宣言することで回避できる。
for文の中で大きいインスタンスを生成してる場合
ARCではインスタンスに自動的にautorelease属性がつくのですが、これは文字どおり自動的に解放してくれるという意味なのですが、解放するタイミングがメインループを抜けるタイミングなので、for文内で作られるインスタンスは全てメモリに蓄積されてしまいます。
これを回避する方法は、以下の様にautoreleasepoolで囲めば良い
autoreleasepool {
//~メモリを多く使用する処理~
}
deinitでリリースされているかの確認
実際アプリを作っていく上で、「あれ?メモリが増え続けてるな」となることがあります。
その場合は怪しいなと思うインスタンスのdeinitにログを仕込んどいてインスタンス解放のタイミングでそのログが出力されるかを確認します。
deinitはいわゆるデストラクタ的なもので、インスタンス解放直前にシステム側から呼ばれるので、これが呼ばれていれば確実に解放されているのがわかります。
呼ばれていなければ解放されて無いので、循環参照が無いか等の確認の為に、このインスタンスの参照を保持している変数を片っぱしから調べに行くわけですね。