Objective-Cにおけるシングルトンのお話
どうもこんばんは、south37です。前回に引き続き今回もObjective-Cネタです。 iOSアプリ開発をしてるとシングルトンが活躍するケースがよくある(らしい)のですが、シングルトン生成のコードがパッと理解出来なくて悶々としたので、ここにメモとして残しておきます。
シングルトン
シングルトンは、あるクラスのインスタンスが単一である事を保証するデザインパターンです。iOSアプリ開発における利用例としては、例えばAFNetWorkingというライブラリのAFNetworkActivityIndicatorManager
というクラスにおいて、シングルトンが使われています。
以下が、実際の実装です。
1 // "AFNetworkActivityIndicatorManager.m" 2 + (instancetype)sharedManager { 3 static AFNetworkActivityIndicatorManager *_sharedManager = nil; 4 static dispatch_once_t oncePredicate; 5 dispatch_once(&oncePredicate, ^{ 6 _sharedManager = [[self alloc] init]; 7 }); 8 9 return _sharedManager; 10 }
解説は後でしますが、単一のインスタンスを返すようなクラスメソッドsharedManager
を定義しているのがポイントです。[AFNetworkActivityIndicatorManager sharedManager]
のように呼び出す事で使用され、いつsharedManager
を呼び出しても同一のインスタンスが返されます(シングルトン)。
シングルトンは、上記の例のように~Managerみたいな名前のクラスに使われている印象です。確かに、Managerがいっぱいいても何か変な気はします。
シングルトンの実装コードの解説
さて、上記コードのうち、自分が疑問だったのはstatic指定子がついた変数の宣言の部分でした。特に、_sharedManager
がメソッド呼び出しの度にnil
に初期化される様に思えて、全然シングルトンにならないじゃないかとか思ってました。
これは、もちろん僕の勘違いでした。
static指定子の変わった性質
結論としては、static
指定子のついた変数の宣言&初期化は一度しか実行されません。static
変数は一度生成されるとずっとメモリに残る(メソッド内で寿命が閉じていない)ため、次にsharedManager
メソッドを呼び出した時にはstatic
変数の宣言のコードは無視されるのです。
[参考: The Static Keyword] http://rypress.com/tutorials/objective-c/functions.html
ちなみに、メソッド内でstatic
指定子をつけて宣言された変数はstatic local variable
と呼ばれ、スコープは宣言したメソッドで閉じているものの寿命は長いという性質を持っています。その意味で、クラス変数の代わりとして使われるstatic variable
とは役割が違うようです。
[参考: Static local variables] http://en.wikipedia.org/wiki/Local_variable
という訳で、_sharedManager
については最初にnilで初期化されてるだけだという事が分かりました。
次は、dispatch_once_t
型とかdispatch_once
関数とかに着目してみましょう。
dispatch_once: 一度しか実行されない事を保証する
実は、
static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ // 何かしら一度しか実行しない処理 });
というのは一度しか実行されない処理を書く為のイディオムとなっています。dispatch_once_t
型というのがlong
型(int
型とほぼ同じ)の別名で、onePredicate
変数が初期化時に0
, 一度dispatch_once
を実行すると-1
になる事でdispatch_once
が実行されたかどうかを識別する為のフラグの役割を果たしています。
これは、NSLog
してみると分かり易いです。確かに、dispatch_once
実行後に-1
になっている事が分かります。
static dispatch_once_t oncePredicate; NSLog(@"%d", oncePredicate); dispatch_once(&oncePredicate, ^{ // 何かしら一度しか実行しない処理 }); NSLog(@"%d", oncePredicate); // コンソール出力 // 2014-07-13 02:43:00.386 Hello[30133:60b] 0 // 2014-07-13 02:43:00.388 Hello[30133:60b] -1
[参考: Grand Central Dispatch (GCD) Reference] https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once
このイディオムを使えば、複数スレッドからの呼び出しに対しても一度しか処理が行われない事が保証されるらしいです。とっても素晴らしいですね!!
まとめ
シングルトンを生成する為のお決まりのパターンが分かりました。昔はシングルトンのインスタンスがnilかどうか(初期化済みかどうか)でif分岐するという方法がとられていた様ですが、スレッドセーフでは無かった為、現在ではdispatch_once
を使う方がオススメのようです。
おまけ: C言語とかでのstatic
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10102644985
とかにも載ってる通り、static宣言が一度しか実行されないというのはC系言語ではむしろ普通の振るまいのようですね。普段LLばっか触ってるんで完全に無知でした。。。反省。。。