SlideShare ist ein Scribd-Unternehmen logo
1 von 62
Downloaden Sie, um offline zu lesen
iPhone輪講
第12回
CHAPTER05
リファレンスカウンタを用いた
メモリ管理方式
C言語の場合malloc()などを用いて自前でメモリ管理を行わなけ
ればならない
確保したメモリ領域は,プログラム終了時点で自動的に
解放される
しかし,小規模な使い捨てのプログラムでない限り,
メモリ領域は不要になった時点で解放する必要がある
メモリ管理の必要性
メモリ管理の必要性
不要になったが,不注意やプログラム上の不備から,メモリ領域
が解放されずそのまま残る事がある
解放し損なった領域は,それぞれが小さいものでも積み重なり
やがてメモリ空間を圧迫するようになる
‣ プログラムの動作が極端に遅くなったり,システム全体の進行
が滞ったりする
‣ このような解放漏れをメモリリークという
メモリ管理の必要性
一旦解放したメモリ領域に後から誤ってアクセスしてしまう
この場合プログラムが誤作動したり,異常終了したりする事が
ある
既に解放された場所を指す危険なポインタを
ぶらさがりポインタと呼ぶ
カウンタ管理方式
Cocoa環境のObjCでは動的なメモリ管理のために
リファレンスカウンタを利用する方式が用意されている
各インスタンスオブジェクトごとに,それが何ヶ所から参照
されているかを示すカウンタを用意する
どこからも参照されなくなったときに解放する
カウンタの操作はプログラマがコード中で指定しなければ
ならない
ガーベジコレクション
ObjC2.0ではカウンタ管理方式とは別にガベージコレクション
というメモリ管理方法も利用できる
ガベージコレクションではプログラムの通常の実行とは
別に不要になったオブジェクトを探すためのルーチンが
自動的に動作して解放が行われる
カウンタ操作のような手間が無い分プログラムの作成が
容易
リファレンスカウンタ
リファレンスカウンタとは,そのインスタンスが何ヶ所から参照
されているか(プログラム中の何ヶ所でそのオブジェクトを必要
としているか)示すもの
リファレンスカウンタ
allocで生成されイニシャライザで初期化されたインスタンスは
それを生成したメソッドを持つオブジェクトから参照されている
リファレンスカウンタは1となる
リファレンスカウンタ
あるオブジェクトAがインスタンスBを参照しながら処理を
進めるとする
インスタンスBが別のオブジェクトから勝手に解放されてしま
わないよう,オブジェクトAはインスタンスBに対して
retainというメッセージを送る
retainを実行するたび,インスタンスのリファレンスカウン
タは1ずつ増加する
リファレンスカウンタ
インスタンスが不要になったらreleaseというメッセージを送る
reelaseはインスタンスのリファレンスカウンタを1ずつ
減らすように働く
リファレンスカウンタ
実際にインスタンスオブジェクトを解放するのはdeallocという
メソッド
releaseが送られ,リファレンスカウンタが0になった時点で
deallocが呼び出されインスタンスは解放される
通常はdeallocというメソッドをプログラム内から
直接呼び出してはいけない
リファレンスカウンタ
retain,release,deallocは以下のように宣言する
retainはレシーバ自身を返り値にする
- (id)retain
- (oneway void)release
- (void)dealloc
リファレンスカウンタ
インスタンスにretainを送る事を「インスタンスを保持する」
という
インスタンスを生成したりインスタンスを保持している状態を
そのインスタンスに対する「オーナーシップを持っている」と
いう
オーナーシップを持つオブジェクトを,そのインスタンスの
オーナーと呼ぶ
リファレンスカウンタ
図5-1:リファレンスカウンタを利用してない例
オブジェクトA
オブジェクトB
1 1 0
オブジェクトA
オブジェクトB
オブジェクトA
オブジェクトB
alloc+init release
(1)オブジェクトAが
インスタンスを生成
(2)オブジェクトBが
インスタンスを参照
(3)オブジェクトAが
インスタンスの利用をやめる
リファレンスカウンタ
(1)ではオブジェクトAの
メソッドの中で,あるインスタ
ンスが生成されてオブジェクトA
のインスタンス変数から
参照するようになったとする
オブジェクトA
オブジェクトB
1
alloc+init
(1)オブジェクトAが
インスタンスを生成
インスタンス
リファレンスカウンタ
インスタンスの持つ
リファレンスカウンタの値は1で
オブジェクトAがオーナーである
とみなせる
オブジェクトA
オブジェクトB
1
alloc+init
(1)オブジェクトAが
インスタンスを生成
オーナー
リファレンスカウンタ
次にこのインスタンスへの
ポインタがオブジェクトBに
渡され,オブジェクトBも同様に
インスタンス変数から参照する
ようになる
1
オブジェクトA
オブジェクトB
(2)オブジェクトBが
インスタンスを参照
リファレンスカウンタ
しかし,オブジェクトBはretain
を送らないので,インスタンス
のオーナーでなくリファレンスカ
ウンタも1のまま
1
オブジェクトA
オブジェクトB
(2)オブジェクトBが
インスタンスを参照
リファレンスカウンタ
オブジェクトAはもうこのインス
タンスを使わなくなったため
releaseメッセージを送信する 0
オブジェクトA
オブジェクトB
release
(3)オブジェクトAが
インスタンスの利用をやめる
リファレンスカウンタ
オブジェクトAはもうこのインス
タンスを使わなくなったため
releaseメッセージを送信する
0
オブジェクトA
オブジェクトB
release
(3)オブジェクトAが
インスタンスの利用をやめる
リファレンスカウンタ
オブジェクトAはもうこのインス
タンスを使わなくなったため
releaseメッセージを送信する
オブジェクトBから参照されて
いるにも関わらず,インスタ
ンスは解放される
0
オブジェクトA
オブジェクトB
release
(3)オブジェクトAが
インスタンスの利用をやめる
リファレンスカウンタ
このような事を防止するには
動的に生成されたインスタンスに対し,それを参照する側の
オブジェクトがretainを送っていなければならない
retainを送る事により,そのインスタンスのオーナーとなり
リファレンスカウンタを増やして,使用中にインスタンスが
解放されないようにする
リファレンスカウンタ
図5-2:リファレンスカウンタを利用した例
オブジェクトA
オブジェクトB
1 2 1
オブジェクトA
オブジェクトB
オブジェクトA
オブジェクトB
alloc+init release
retain
(1)オブジェクトAが
インスタンスを生成
(2)オブジェクトBが
インスタンスを参照
(3)オブジェクトAが
インスタンスの利用をやめる
リファレンスカウンタ
(2)でオブジェクトBがインス
タンスを参照すると同時に
retainを送ってインスタンスを
保持する
この時点でリファレンスカ
ウンタは2になる
2
オブジェクトA
オブジェクトB
retain
(2)オブジェクトBが
インスタンスを参照
リファレンスカウンタ
オブジェクトAがreleaseを送信
してオーナーシップを放棄しても
インスタンスは解放されない
オブジェクトBはその後も
このインスタンスを使う事が
出来る
1
オブジェクトA
オブジェクトB
release
(3)オブジェクトAが
インスタンスの利用をやめる
インスタンス解放のための
メソッド定義
オーナーシップを放棄するにはオブジェクトにreleaseを送る
実際にインスタンスに割り当てられたメモリ領域が解放される
時にはdeallocというメソッドが実行される
インスタンス解放のための
メソッド定義
あるインスタンスを解放する
保持していたオーナーシップを放棄するには
‣ スーパークラスのdeallocのメソッドを上書き
‣ その中でそのクラス固有のインスタンスに対してrelease
メッセージを送る
インスタンス解放のための
メソッド定義
releaseのメソッドを上書きしてはいけない
実際に解放されるのはdeallocが呼び出されたとき
後始末はすべてそこで行う
インスタンス解放のための
メソッド定義
上書きしたメソッドの中で
まずインスタンスを解放す
る直前にすべき後始末の
コードを記述する
- (void)dealloc //deallocを上書き
{
/*
ここでサブクラスに固有の
インスタンス変数の
オーナーシップをreleaseを使って
放棄する
その他の「後始末」のコードもここに
書く
*/
[super dealloc];
}
インスタンス解放のための
メソッド定義
通常はそのサブクラスのイ
ンスタンス変数に対応する
オブジェクトのオーナー
シップをreleaseを使って
放棄する
- (void)dealloc //deallocを上書き
{
/*
ここでサブクラスに固有の
インスタンス変数の
オーナーシップをreleaseを使って
放棄する
その他の「後始末」のコードもここに
書く
*/
[super dealloc];
}
インスタンス解放のための
メソッド定義
その後でスーパークラスの
deallocメソッドを呼びだし
その他のインスタンス変数
の処理やメモリ領域の解放
を任せる
- (void)dealloc //deallocを上書き
{
/*
ここでサブクラスに固有の
インスタンス変数の
オーナーシップをreleaseを使って
放棄する
その他の「後始末」のコードもここに
書く
*/
[super dealloc];
}
インスタンスの自動解放
実際のプログラミングでは,一時的にだけ利用して,あとは不要
となるオブジェクトを多数使う事は少なくない
そのような一時的なインスタンスを毎回忘れずに解放するのは
面倒
インスタンスの自動解放
Cocoa環境のObjCにはインスタンスの自動解放という仕組みが
用意されている
自動解放をおこなうには,まずNSAutoreleasePoolのインス
タンスを作る
このインスタンスを自動解放プールと呼ぶ
インスタンスの自動解放
その状態でautoreleaseメソッドを呼び出すと,そのインスタ
ンスが自動解放プールに登録される
この時点ではインスタンスは解放されない,リファレンスカ
ウンタの値も変化しない
最後に自動解放プールを解放すると,登録されているすべての
releaseメソッドを呼び出す
インスタンスの自動解放
autoreleaseは以下のように宣言される
自動解放プールに登録され,事実上はオーナ湿布を放棄されて
いるオブジェクトを一時的なオブジェクトと呼ぶ事にする
- (id)autorelease;
インスタンスの自動解放
自動解放プールの典型的な使い方
id pool = [[NSAutoreleasePool alloc]init];
/*
ここで一連の作業を行う
一時的に用いられるインスタンスにはautorelease
メッセージを送っておく
*/
[pool release]; /*インスタンスはここで解放される */
自動解放プールの使い方の注意
自動解放プールのインスタンスは複数作成できる
複数の自動解放プールが存在する場合,最も最近作成されたも
のが有効なものとして機能する
自動解放プールの使い方の注意
長時間実行されるルーチンや,一時的なインスタンスを多く使う
ルーチンでは局所的に自動解放プールを利用し,メモリ資源がよ
り有効に使われるようにプログラムを記述する
自動解放プールの使い方の注意
何回も繰り返されるループ内で一時的なインスタンスが数多く使
われるような場合
ループの最初で自動解放プールを作成し,ループの最後で解放
する
while (...) {
id pool = [[NSAutoreleasePool alloc]init];
/*
ここで一連の作業を行う
*/
[pool release]; /*インスタンスはここで解放される */
}
イベントループと
自動解放プール
マウスやキーボードの操作が行われたという情報はイベントと
呼ばれる
アプリケーションは発生したイベントを次々に取り出しては
それに対応する処理を行うという動作を繰り返す
この繰り返し動作はイベントループと呼ばれる
CocoaのGUIを用いたアプリケーションではNSApplication
というクラスがイベントループの管理を行う
イベントループと
自動解放プール
例えばあるボタンがクリックされる
‣ NSApplicationのインスタンスがそのイベントを検知する
‣ ボタンのクリックに対応したメッセージがあらかじめ指定
したオブジェクトに送られて処理が行われる
イベントループと
自動解放プール
NSApplicationはイベントループの各処理の前に自動解放ループ
を作成し,処理ルーチンの実行が終わった後で自動解放プールを
解放する
CocoaのGUIを利用したプログラミングでは,自動解放プール
の管理に気を使うことなく,一時的なインスタンスを自由に
使う事が出来る
オーナーシップ・ポリシー
オーナーシップ・ポリシーとは
「インスタンスイブジェクとのオーナーが,そのインスタンス
の解放に関して責任を持つ」とする取り決めの事
言語上の決まりでなく,マナーのようなもの
オーナーシップ・ポリシー
オブジェクトが別のオブジェクトのオーナーになるとき
alloc...によるインスタンス生成
クラスに対してallocで始まるクラスメソッドを送ってイン
スタンスを生成した場合
あるいはnew...で始まるクラスメソッドを送って
インスタンスを生成した場合
オーナーシップ・ポリシー
copy...によるインスタンスのコピー
あるインスタンスに対してcopyで始まるメソッドを送って
インスタンスの複製を生成した場合
retainによる保持
あるインスタンスにretainメッセージを送って保持した場合
オーナーシップと
オブジェクト間の関係
プログラムを複数のオブジェクトから構成するとき,
どのオブジェクトがどのオブジェクトのオーナーとなるべきか
決めておく必要がある
容易に決定できる場合と,そうでない場合がある
オーナーシップと
オブジェクト間の関係
図:オブジェクト間の参照関係
B
A
C
E
D
F
B
A
C
D
(1)階層的な包含関係を
持つ場合
(2)ネットワーク的な参照関係を
持つ場合
オーナーシップと
オブジェクト間の関係
(1)は親とも言うべきオブ
ジェクトが子に相当する
オブジェクトを保持すると
いう階層的な関係となって
いる場合
B
A
C
E
D
F
(1)階層的な包含関係を
持つ場合
オーナーシップと
オブジェクト間の関係
オブジェクトAはB,C,D
のオーナーであり,
DはE,Fのオーナーである
と考える
B
A
C
E
D
F
(1)階層的な包含関係を
持つ場合
オーナーシップと
オブジェクト間の関係
Aを解放するときにはA自体
の解放に先立ってB,C,D
を解放する
Dを解放するには,まず
E,Fを解放するという
順番になる
B
A
C
E
D
F
(1)階層的な包含関係を
持つ場合
オーナーシップと
オブジェクト間の関係
CからA,FからDに点線矢
印がのびている
自分の親に相当するオブ
ジェクトを参照するため
のポインタで
バックポインタと呼ばれ
る
B
A
C
E
D
F
(1)階層的な包含関係を
持つ場合
オーナーシップと
オブジェクト間の関係
(2)は複数のオブジェクトが
比較的対等な立場で相互に
参照し合っている例
B
A
C
D
(2)ネットワーク的な参照関係を
持つ場合
オーナーシップと
オブジェクト間の関係
すべての参照関係にオーナー
シップが伴っていると
容易に保持の循環問題が発
生する
B
A
C
D
(2)ネットワーク的な参照関係を
持つ場合
オーナーシップと
オブジェクト間の関係
オーナーシップを伴わない
参照関係の場合,注意しな
いと知らないうちに相手が
解放されてしまう
B
A
C
D
(2)ネットワーク的な参照関係を
持つ場合
オーナーシップと
オブジェクト間の関係
いずれにせよオーナーシップを保持するインスタンス変数と
ポインタを参照するだけのインスタンス変数があった場合
その区別を厳密に行わなければメモリリークが発生する
原因となる
一時的なインスタンスの生成
一時的に使われる事を前提に,オーナーを持たない形で生成され
るインスタンスもある
生成直後に自動解放プールに登録する事で実現される
一時的なインスタンスの生成
文字列を表すためのNSStringがある
文字コードがUnicode(UTF8)のCの文字列からNSString
のインスタンスを生成することができる
一時的なインスタンスの生成
以下の2つがある
allocで生成したインスタンスに対するイニシャライザ
生成したオブジェクトがインスタンスのオーナーになる
一時的なインスタンスを生成するためのクラスメソッド
結果として返されるインスタンスは自動解放プールに
登録される
- (id)initWithUTF8String:(const char *)bytes
- (id)stringWithUTF8String:(const char *)bytes
一時的なインスタンスの生成
一時的なインスタンスを生成するクラスメソッドをもつクラスは
他にもある
一般にこれらのメソッドはinitではなく,生成されるデータを
表す名前から始まる約束がある
NSStringならメソッド名はstring...から始まる
- (id)stringWithUTF8String:(const char *)bytes
解放しないオブジェクト
オブジェクトによっては解放しなくてよいものもある
クラスオブジェクトや,1つのインスタンスだけ共有して
使うもの,NSString文字列になるようなオブジェクト定数
解放しないオブジェクト
解放してほしくないオブジェクトを定義するには
クラス定義でメソッドretain,release,およびretainCountを
書き換える
具体的にはretainとrelease,autoreleaseが何もしないように
上書きし,リファレンスカウンタの値が変更されないように
する

Weitere ähnliche Inhalte

Mehr von hasegawa

Cocoa Pro08
Cocoa Pro08Cocoa Pro08
Cocoa Pro08hasegawa
 
Cocoa Pro07
Cocoa Pro07Cocoa Pro07
Cocoa Pro07hasegawa
 
Cocoa Pro5
Cocoa Pro5Cocoa Pro5
Cocoa Pro5hasegawa
 
Cocoa Pro4
Cocoa Pro4Cocoa Pro4
Cocoa Pro4hasegawa
 
Cocoa Pro01
Cocoa Pro01Cocoa Pro01
Cocoa Pro01hasegawa
 
Cocoa Pro03
Cocoa Pro03Cocoa Pro03
Cocoa Pro03hasegawa
 
CocoaPro02
CocoaPro02CocoaPro02
CocoaPro02hasegawa
 

Mehr von hasegawa (7)

Cocoa Pro08
Cocoa Pro08Cocoa Pro08
Cocoa Pro08
 
Cocoa Pro07
Cocoa Pro07Cocoa Pro07
Cocoa Pro07
 
Cocoa Pro5
Cocoa Pro5Cocoa Pro5
Cocoa Pro5
 
Cocoa Pro4
Cocoa Pro4Cocoa Pro4
Cocoa Pro4
 
Cocoa Pro01
Cocoa Pro01Cocoa Pro01
Cocoa Pro01
 
Cocoa Pro03
Cocoa Pro03Cocoa Pro03
Cocoa Pro03
 
CocoaPro02
CocoaPro02CocoaPro02
CocoaPro02
 

Objc03 1