現在位置: ホーム / TinBlog / zeroing weak reference

zeroing weak reference

作者: h2 最終変更日時 2012年02月15日 21時10分 |
カテゴリー: ,
iOS5になってから、オブジェクトの削除を自動でやってくれる機能が搭載された。らしい。しかも、この機能、有り難いことにiOS4にさかのぼって動作してくれるそうな。そういうことなら、現時点で使わない手はないわけで。だが、ちょっと真面目に調べてみたところ、聞き慣れないzeroing weak referenceなるものに出くわした。

ところで、オブジェクトの自動掃除といえば、真っ先に思い浮かぶのが、ガーベジコレクタ。Androidで使われているjava言語でおなじみ、gc() のことだ。じつは、ガーベジコレクタの機能自体は、Objective-Cでも2.0で既に導入されていて、Mac OSXでは普通に使われている機能だったりする。ところが、iOSの開発言語は、同じObjective-Cでも「1.0」相当、つまり、ガーベジコレクタの機能は、なかったわけですな。

なぜかというと、このガーベジコレクト、便利ではあるものの、使用メモリ量の厳密なコントロールが難しかったり、不要オブジェクトの判定などでCPUの処理時間が結構喰われるなど、コストが高くつくので、初期iPhoneのような貧弱なハードでは荷が重かったんですな。

実際のところ、現時点の最新Androidモデルでも、gc()がらみで操作がもたつく場面に出くわすことがある。

そんなわけで、iPhoneのプログラムでは、ガーベジコレクタを採用してこなかったわけだけど、それだとメモリマネージメントが大変だと言うことだからだろう、iOS5になって、ガーベジコレクタに代わるメモリ管理、ARC (Automatic Reference Counting) が導入された。ただし、このARC、「ガーベジコレクタの代わり」とはいっても、ガーベジコレクタのように「アプリケーションの実行中に、不要となったメモリがないかを確認して削除」するのではなく、コンパイル、つまりプログラムのソースコードをアプリケーションに変換するときに、どのタイミングで不要オブジェクトを削除(メモリを解放)するのかを決める、というもの。

・・・要するに、今まではプログラム制作者がセコセコ書き込んでいたretain/releaseのコードを、最終的にアプリケーションに仕上げるためのソフト(コンパイラ)が肩代わりしてくれるわけだ。こうすることで、実際にユーザーがアプリを動かすときは、今まで通りプログラムレベルでメモリマネジメントが完了している状態のアプリと(ほぼ)同様に実行できる。また、アプリ実行の合間合間でガーベジコレクタ(メモリの自動掃除機)を動かす必要もないので、CPU処理能力などが犠牲になることもない。

 

ただし。幾つか制限がある。そのうちの一つが、プログラムの書き方。Objectiv-C2.0で実装されている方式だと、ガーベジコレクタを使用する/しないにかかわらず、コードの記載方法に差はない(retain/releaseは単に無視される)のだが、ARCでは、retain/release等の記載があるとエラーとなる。詳しくはADCのTransitioning to ARC Release Notesを参照。

 

そして、このページのなかに、ARCがiOS4で動作しない制限として、weak referenceが挙げられている。直訳すると「弱い参照」だが、意味としては「信頼性のない参照」とでもした方がいい。このweak referenceは、なにもObjective-C独特のものというわけではなく、C言語の構造体(struct)に対する動的なポインタとか、C++でdeleteされうるインスタンスへのポインタとか、そういうものをさしている。

例えば、以下のコード

static ClassA	*ref = NULL;

void createFunc() {
	ref = new ClassA();
}
void deleteFunc() {
	delete ref;
	ref = NULL;
}
ClassA* getFunc() {
	return ref;
}

....
class ClassB {
	ClassA *myRef = NULL;
	...
	inline void func1() {
		myRef = getFunc();
	}
}

なんてのを考えた場合(エラーを起こし易いコードだとか、ポインタのNULL判定が甘いだとかはさておき)。このコードで言うところの、ClassB::myRef が、「信頼性のない参照」にあたる。たとえ「func1()」で正常なClassAのインスタンスポインタが取得できたとしても、そのあと、どのタイミングでこのインスタンスが破棄されるか(deleteFunc(); が呼び出されるか)、保証がないわけだ。

厳密に言えば、C++のインスタンスなんて全て「信頼性のない参照」と言えなくもないが・・・

Objective-Cでも、このような参照を配置することができる。つまり、「retainしないでポインタを確保」するようなコードだ。実例を挙げると、

@interface ClassB {
	ClassA *myRef;
}
@property (assign) ClassA *myRef;
@end

など。

このような参照に対しては、ARCはiOS4ではサポートされないよ、というわけだ。

 

さて、ここで、ようやく本題。先ほどのADCのページのなかに、初耳の単語が登場した。タイトルにあるzeroing weak referenceだ。いったいなんじゃらほい、と、ネットを探し回ったところ、こんな解説を見つけた。

Zeroing weak references eliminate this danger. They work just like a regular weak reference, except that when the target object is destroyed, they automatically become nil. At any time you access an object through a zeroing weak reference, you're guaranteed to either access a valid, live object, or get nil. As long as your code can handle nil, then you're perfectly safe.
--- Zeroing Weak References in Objective-C (mikeash.com)

・・・うむ、大変わかりやすい解説。

つまり、だ。「zeroing weak referenceとして設定されたポインタは、割り当てられたインスタンスが破棄された段階で、自動的にnil(NULL, つまりゼロ、'0')でクリアされるよ」ということだ。前述のC++の例で言えば、deleteFunc(); が呼び出されたタイミングでstaticのrefにはNULLが代入されるが、同時に、ClassBのインスタンス(10個あれば10個分)のmyRefにも、自動でNULLが代入される、ということ。

 

・・・うーむ。便利なような、そうでないような。

使いどころが難しい。それに、まだ気になる部分がちらほら。たとえば、マルチスレッドでほぼ同時に参照とdeleteが発生したらどうなるんだろう、とか。

実際に活用する前に、もう少し検証が必要そうだ。

« 2019 年 7月 »
7月
123456
78910111213
14151617181920
21222324252627
28293031