CLLocationManager
フレームワークの
CLLocationManager
クラスは、すべてのGPSモジュールサービスへの単一のエントリポイントを提供します。 誰かが考えるかもしれません:
CLLocationManager
コピーを作成できる場合、アプリケーションの追加のGPSサービスのセットを取得することは可能ですか? 幻想のように聞こえます-1つのハードウェアの価格で2つのGPSソフトウェアを作成しました。 しかし実際には、iPhoneにはGPSが1つしかないため、一度に1つのGPSしか取得できません。これは衛星との実際の接続を作成します。 そのため、2つの別々のGPS接続を同時に操作できるスーパーアプリケーションを作成し、それを友人に自慢したい場合は、よく考えてください。
オブジェクト指向アプリケーションのシングルトンクラスは、常に同じインスタンスを返します。 クラスオブジェクトが提供するリソースにグローバルアクセスポイントを提供します。 この機能を持つパターンはシングルトンと呼ばれます。
この章では、Objective-CおよびCocoa Touch iOSフレームワークでSingletonパターンを実装および使用する可能性を探ります。
シングルトンパターンはどのようなものですか?
シングルトンパターンは、おそらく最も単純なパターンです。 その目的は、クラスオブジェクトをシステム内の唯一のインスタンスにすることです。 まず、クラスの複数のインスタンスの作成を禁止する必要があります。 これを行うには、ファクトリメソッド(第4章)を使用できます。これは、クラスのインスタンスが別の単一インスタンスを作成することを許可する意味がないため、静的である必要があります。 図7-1は、シングルトンクラスの構造を示しています。

図7-1。 シングルトンパターンの静的構造。
静的な
uniqueInstance
は、
Singleton
クラスの唯一のインスタンスであり、静的
sharedInstance
メソッドがクライアントに返すクラス変数として表されます。 通常、
uniqueInstance
は
uniqueInstance
が
uniqueInstance
されているかどうかを確認し
uniqueInstance
。 そうでない場合、メソッドは返す前に作成します。
ご注意 シングルトンパターン:クラスのインスタンスが1つしかないことを確認し、単一のアクセスポイントを提供します。
* GoFの「デザインパターン」(Addison-Wesley、
1994)。
シングルトンパターンはいつ使用できますか?
次の場合、Singletonパターンの使用を検討するのは理にかなっています。
- ファクトリメソッドなどの既知のアクセスポイントを介してアクセスできる必要があるシステム内のクラスのインスタンスは1つだけです。
- 単一のインスタンスは継承によってのみ拡張でき、クライアントコードは拡張オブジェクトを使用してもパフォーマンスを失うことはありません。
シングルトンパターンは、共有リソースにアクセスする便利な方法で一意のインスタンスを作成するための使い慣れた方法を提供します。 静的なグローバルオブジェクトへの参照を使用する方法は、同じクラスの別のインスタンスの作成を妨げません。 クラスメソッドを使用するアプローチは、グローバルアクセスポイントを提供できますが、コード分離の柔軟性に欠けます。
静的グローバル変数には、クラスのインスタンスへの単一の参照が含まれます。 アクセスできる別のクラスまたはメソッドは、同じ変数を使用する他のクラスまたはメソッドと実際に同じコピーを共有します。 必要なもののように見えます。 アプリケーション全体で同じグローバル変数が使用されている限り、すべてが素晴らしく見えます。 したがって、実際には、Singletonパターンは必要ありません。 しかし、待ってください! あなたのチームの誰かがあなたのコードであなたと同じグローバル変数を定義したらどうしますか? その後、1つのアプリケーションに同じグローバルオブジェクトの2つのコピーがあります。したがって、グローバル変数は実際に問題を解決しません。
クラスメソッドは、オブジェクトを作成せずに分割する機能を提供します。 クラスメソッドでは、リソースの単一インスタンスがサポートされています。 ただし、クラスがより多くの機能を提供するために継承を必要とする場合、このアプローチには柔軟性がありません。
シングルトンクラスは、単一のクラスオブジェクトを作成してアクセスするための、単一で一貫性のある有名なアクセスポイントを保証できます。 このパターンは、そのサブクラスのいずれかがインスタンス作成メソッドをオーバーライドし、クライアントコードを変更せずにクラスオブジェクトの作成を完全に制御できるような柔軟性を提供します。 さらに良いことに、インスタンス化メソッドの実装は、オブジェクトの作成を動的に処理できます。 クラスの実際のタイプは、正しいオブジェクトが作成されていることを確認するために、実行時に決定できます。 この手法についてはさらに説明します。
シングルトンパターンの柔軟なバージョンもあります。このメソッドでは、ファクトリメソッドは常に同じインスタンスを返しますが、他のインスタンスを割り当てて初期化することもできます。 この制限の少ないパターンのバージョンについては、この章で後述する「NSFileManagerクラスの使用」セクションで説明します。
Objective-Cでのシングルトン実装
シングルトンクラスを正しく設計することを検討する価値があります。 最初の質問は、クラスのインスタンスを1つだけ作成できるようにする方法です。 C ++やJavaなどの他のオブジェクト指向言語で記述されたアプリケーションのクライアントは、そのコンストラクターがprivateとして宣言されている場合、クラスオブジェクトを作成できません。 Objective-Cはどうですか?
Objective-Cのメソッドはすべて開かれており、言語自体は動的に型指定されているため、どのクラスでも、コンパイル時に重要なチェックなしでメッセージを別のクラス(C ++およびJavaのメソッド呼び出し)に送信できます(メソッドが宣言されていない場合はコンパイラー警告のみ)。 Cocoaフレームワーク(Cocoa Touchを含む)は、参照カウントによるメモリ管理を使用して、メモリ内のオブジェクトのライフタイムを維持します。 これらの機能はすべて、Objective-Cでのシングルトンの実装をかなり困難にします。
デザインパターンの元の例では、シングルトンC ++の例はリスト7-1のようになりました。
リスト7-1。 本Design PatternsのSingletonパターンの単一のC ++サンプル。
class Singleton { public: static Singleton *Instance(); protected: Singleton(); private: static Singleton *_instance; }; Singleton *Singleton::_instance = 0; Singleton *Singleton::Instance() { if (_instance == 0) { _instance = new Singleton; } return _instance; }
本で説明されているように、C ++での実装はシンプルで簡単です。 静的
Instance()
メソッドでは、静的
_instance
変数の0(
NULL
)
_instance
チェックされ
NULL
。 その場合、新しい
Singleton
オブジェクトが作成され、返されます。 Objective-Cのバージョンはあなたの兄弟とそれほど違わず、リスト7-2および7-3のように見えるはずだと思う人もいるかもしれません。
リスト7–2。 Singleton.hのシングルトンクラス宣言
@interface Singleton : NSObject { } + (Singleton *) sharedInstance; @end
リスト7-3。 sharedInstance Singleton.mメソッドの実装
@implementation Singleton static Singleton *sharedSingleton_ = nil; + (Singleton *) sharedInstance { if (sharedSingleton_ == nil) { sharedSingleton_ = [[Singleton alloc] init]; } return sharedSingleton_; } @end
その場合、これは非常に単純な章であり、Objective-Cで実装された1つのパターンを既に学習しています。 実際、実際のアプリケーションで使用するのに十分な実装の信頼性を実現するには、いくつかの困難を克服する必要があります。 シングルトンパターンの「厳密な」バージョンが必要な場合、実際のコードで使用できるように解決する必要がある2つの主な問題があります。
- 呼び出し元は、他の割り当てツールを使用してシングルトンオブジェクトを作成できません。 それ以外の場合は、Singletonクラスの複数のインスタンスを作成できます。
- シングルトンオブジェクトの作成に関する制限は、参照カウントモデルと一致している必要があります。
リスト7-4は、私たちが目指している実装に近い実装を示しています。 コードは非常に長いので、議論を簡単にするためにいくつかの部分に分けます。
リスト7-4。 Objective-Cでのシングルトンのより適切な実装
#import "Singleton.h" @implementation Singleton static Singleton * sharedSingleton_ = nil; + (Singleton*) sharedInstance { if (sharedSingleton_ == nil) { sharedSingleton_ = [[super allocWithZone:NULL] init]; } return sharedSingleton_; }
sharedInstance
メソッド内では、最初の例のように、クラスの単一のインスタンスが作成されているかどうかが最初にチェックされ、そうでない場合は新しいインスタンスが作成されて返されます。 ただし、今回は
[[super allocWithZone:NULL] init]
を呼び出して、
alloc
などの他のメソッドを使用する代わりに新しいインスタンスを作成します。 なぜ、
self
ではなく
super
か? これは、クラス内のオブジェクトにメモリを割り当てるための基本的なメソッドがオーバーライドされているため、この機能をベースクラス(この場合は
NSObject
)に「貸し」、メモリを割り当てる低レベルのルーチンを実行する必要があるためです。
+ (id) allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; } - (id) copyWithZone:(NSZone*)zone { return self; }
考慮する必要がある
Singleton
クラスのメモリ管理に適用されるいくつかのメソッドがあります。
allocWithZone:(NSZone *)zone
メソッドは、
sharedInstance
メソッドから返されるクラスのインスタンスを返すだけです。 Cocoa Touchフレームワークでは、クラスメソッド
allocWithZone:(NSZone *)zone
を呼び出すと、インスタンス
allocWithZone:(NSZone *)zone
メモリが割り当てられ、その参照カウンターが1に設定され、インスタンスが返されます。
alloc
メソッド
alloc
多くの状況で使用されることを見てきました。 実際、
alloc
は、ゾーンを
NULL
に設定して
allocWithZone:
を呼び出し、デフォルトゾーンのインスタンスにメモリを割り当てます。 オブジェクトの作成とメモリの管理の詳細は、本書の範囲外です。 詳細については、ドキュメントを参照してください。
同様に、
copyWithZone:(NSZone*)zone
メソッドをオーバーライドして、インスタンスのコピーを返さないが、同じコピーを返して
self
返すようにする必要があります。
- (id) retain { return self; } - (NSUInteger) retainCount { return NSUIntegerMax; // , } - (void) release { // } - (id) autorelease { return self; } @end
retain
、
release
autorelease
などの他のメソッドは、return
self
以外は(参照カウントメモリ管理モデルで)何もしないようにオーバーライドされます。
retainCount
メソッドは
NSUIntegerMax
(4,294,967,295)を返し、アプリケーションの存続期間中にインスタンスがメモリから削除されないようにします。
Retain Singletonを呼び出す理由
sharedInstance
メソッドから
allocWithZone:
戻るシングルトンオブジェクトで
retain
を呼び出すことに気付いたかもしれませんが、
retain
オーバーライドされ、実装では実際に無視されます。 これにより、Singletonクラスの「厳密性」を低くすることができます(つまり、追加のインスタンスにメモリを割り当てて初期化できますが、ファクトリの
sharedInstance
メソッドは常に同じインスタンスを返すか、Singletonオブジェクトが破壊可能になります)。 サブクラスは、適切なメモリ管理実装を提供するために、
retain
、
release
および
autorelease
再度オーバーライドできます。
シングルトンパターンの柔軟なバージョンについては、この章で後述する「NSFileManagerクラスの使用」セクションで説明します。
Objective-Cのシングルトンパターンがどのように見えるかについては、すでに十分に詳細に検討しました。 ただし、使用する前に慎重に検討することがもう1つあります。 元の
Singleton
クラスを継承する場合はどうなりますか? これを行う方法を検討してください。
シングルトン継承
alloc
呼び出し
alloc
super
にリダイレクトされるため、
NSObject
クラス
NSObject
オブジェクトのメモリの割り当てを処理します。 変更せずに
Singleton
クラスから継承する場合、返されるインスタンスは常に
Singleton
型になります。
Singleton
クラスはすべてのインスタンス化メソッドをオーバーライドするため、このクラスから継承するのは簡単ではありません。 しかし、私たちは幸運です。 いくつかのFoundation関数を使用して、クラスタイプに基づいてオブジェクトをインスタンス化できます。 それらの1つは
id NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone)
です。 したがって、「シングルトン」というクラスのオブジェクトをインスタンス化する場合、次のことができます。
Singleton *singleton = [NSAllocateObject ([Singleton class], 0, NULL) init];
最初のパラメーターは、
Singleton
クラスのタイプです。 2番目のパラメーターは、インデックス付きインスタンス変数の任意の数の追加バイト用に設計されており、常に0です。3番目のパラメーターは、割り当てられたメモリゾーンの指定です。 ほとんどの場合、デフォルトのゾーンが使用されます(パラメーターは
NULL
)。 したがって、この関数を使用して、クラスのタイプを知っているオブジェクトをインスタンス化できます。
Singleton
クラスから継承するには何をする必要がありますか?
sharedInstance
メソッドの初期バージョンは次のようになっていることを思い出してください。
+ (Singleton*) sharedInstance { if (sharedSingleton_ == nil) { sharedSingleton_ = [[super allocWithZone:NULL] init]; } return sharedSingleton_; }
NSAllocateObject
トリックを使用してインスタンスを作成すると、次のようになります。
+ (Singleton *) sharedInstance { if (sharedSingleton_ == nil) { sharedSingleton_ = [NSAllocateObject([self class], 0, NULL) init]; } return sharedSingleton_; }
Singleton
クラスまたはそのサブクラスをインスタンス化するかどうかは関係ありません。このバージョンはすべてを正しく行います。
スレッドセーフ
この例の
Singleton
クラスは、一般的な使用にのみ適しています。 マルチスレッド環境でシングルトンオブジェクトを使用する場合は、スレッドセーフにする必要があります。 これを行うには、
@synchronized()
ブロックを挿入するか、
sharedSingleton_
インスタンスの静的変数の
nil
チェックの周りに
NSLock
インスタンスを使用します。 保護する必要がある他のプロパティがある場合、それらを
atomic
することができます。
Cocoa Touch Frameworkでのシングルトーンの使用
Cocoa Touch Frameworkのドキュメントに慣れると、多くの異なるクラスのシングルトーンに遭遇することになります。 このセクションでは、
UIApplication
、
UIAccelerometer
、
NSFileManager
一部について
UIAccelerometer
し
NSFileManager
。
UIApplicationクラスの使用
フレームワークで最も一般的に使用されるシングルトンクラスの
UIApplication
は、
UIApplication
クラスです。 これは、iOSアプリケーションの制御と調整の一元化されたポイントを提供します。
各アプリケーションには、
UIApplication
単一のインスタンスがあり
UIApplication
。 これは、アプリケーションの起動時に
UIApplicationMain
関数によってシングルトンオブジェクトとして作成され、
sharedApplication
クラスのメソッドを介して実行時に使用可能になります。
UIApplication
オブジェクトは、着信ユーザーメッセージの初期ルーティングや、
UIControl
クラスのオブジェクトのアクティビティメッセージを対応するターゲットオブジェクトにディスパッチするなど、プログラムの多くのユーティリティタスクを実行します。 開いているすべての
UIWindow
オブジェクトのリストを保持します。 アプリケーションオブジェクトは
UIApplicationDelegate
アプリケーションデリゲートオブジェクトに関連付けられており、起動、メモリ不足の警告、アプリケーションの終了、バックグラウンドプロセスなどのプログラム実行中の重要なイベントが通知されます。 これらのイベントのハンドラーにより、デリゲートはアプリケーションの動作をカスタマイズできます。
UIAccelerometerクラスの使用
Cocoa Touchフレームワークのもう1つの一般的なシングルトンは、
UIAccelerometer
です。
UIAccelerometer
クラスを使用すると、アプリケーションは、iOSデバイスの組み込みの加速度計から加速度関連データを受信するようにサブスクライブできます。 アプリケーションは、3次元空間の主軸に沿った線形加速度の変化に関するデータを使用して、デバイスの現在の向きと瞬間的な向きの変化の両方を判断できます。
UIAccelerometer
クラスはシングルトンなので、そのオブジェクトを明示的に作成することはできません。 唯一のインスタンスにアクセスするには、
sharedAccelerometer
クラス
sharedAccelerometer
呼び出す必要があります。 さらに、
updateInterval
プロパティと
delegate
プロパティを独自のデリゲートオブジェクトに設定して、シングルトンインスタンスから報告された加速データを受信できます。
NSFileManagerクラスの使用
NSFileManager
クラスは、かつてMac OS X 10.5およびiOS 2.0より前のシングルトンパターンの「厳密な」実装でした。 その
init
メソッドを呼び出しても何も行われず、その唯一のインスタンスを作成でき、
defaultManager
クラス
defaultManager
介してのみアクセスできます。 ただし、シングルトン実装はスレッドセーフではないため、このセキュリティを確保するには
NSFileManager
新しいインスタンスを作成する必要があります。 このアプローチは、ファクトリメソッドが常に同じインスタンスを返すシングルトンのより柔軟な実装と見なされますが、追加のインスタンスを選択して初期化することもできます。
「厳密な」シングルトンを実装する場合は、前のセクションで説明した例と同様の実装が必要です。 それ以外の場合、
allocWithZone:
およびその他の関連メソッドをオーバーライドしないでください。
結論
シングルトンパターンは、iOSだけでなく、ほぼすべての種類のアプリケーションで最も広く使用されているパターンの1つです。
シングルトンは、アプリケーションサービスを調整するために集中型クラスが必要な場合に役立ちます。
この章では、オブジェクトの作成に関するこのパートの終わりを示します。 次のパートでは、異なるインターフェイスを持つオブジェクトの適応/統合に焦点を当てたいくつかのデザインパターンを見ていきます。