iOSのシングルトンの話

  • 295
    いいね
  • 5
    コメント
この記事は最終更新日から1年以上が経過しています。

皆さんiOSでシングルトンは使っていますか?
シングルトンはいわゆるデザインパターンと呼ばれるもので、複数のクラス間の変数、オブジェクト、メソッドのやりとりを劇的に簡単にすることが可能です。
このように便利ですが今ひとつとっつきずらいiOSのシングルトンについてこの記事では少々解説したいと思います。

シングルトンを使用したクラスには、例えば以下のメリットが有ります。
・複数のクラス間での変数やオブジェクトの共有が簡単にできる。
・複数のクラス間でメソッドの共有ができる。
・ViewControllerの機能を分担させることができる。

一方で、以下のようなデメリットも有ります。
・不要になっても自動的に解放されない。
・何度もインスタンス化することができない。

必要に応じて、通常のクラスとシングルトンを適用したクラスを使い分けるといいと思います。

シングルトンですが、シングルスレッドの場合は次のように書くことができます。

SingletonSample.h
@interface SingletonSample : NSObject
+ (SingletonSample *)sharedManager;
@end
SingletonSample.m
@implementation SingletonSample

static SingletonSample *sharedData_ = nil;

+ (SingletonSample *)sharedManager{
    if (!sharedData_) {
         sharedData_ = [SingletonSample new];
    }
    return sharedData_;
}

- (id)init
{
    self = [super init];
    if (self) {
        //Initialization
    }
    return self;
}

他のクラスからシングルトンのクラス内のプロパティやメソッドにアクセスするためには、以下のように記述します。

    [SingletonSample sharedManager].(プロパティ名) 
    [[SingletonSample sharedManager] (メソッド名)];

シングルトンではインスタンスの初期化が最初の1回しか行われず、それ故に1つのインスタンスを共有できることがメリットですが、マルチスレッドの場合は複数のスレッドが同時に初期化を行ってしまう危険性があります。
そのため、以下のように@synchronizedを使ったスレッドセーフな記述をした方がベターかと思います。

SingletonSample.m
@implementation SingletonSample

static SingletonSample *sharedData_ = nil;

+ (SingletonSample *)sharedManager{
    @synchronized(self){
        if (!sharedData_) {
            sharedData_ = [SingletonSample new];
        }
    }
    return sharedData_;
}

- (id)init
{
    self = [super init];
    if (self) {
        //Initialization
    }
    return self;
}

もしくは、以下の通りにdispatch_once_tを使ってアプリの起動中初期化処理は1回のみにするという手もあります。これもスレッドセーフです。

SingletonSample.m

@implementation SingletonSample

static SingletonManager* sharedData_ = nil;

+ (SingletonSample*)sharedManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedData_ = [SingletonSample new];
    });
    return sharedData_;
}

それでは、@synchronizedとdispatch_once_tを使った方法が全く一緒なのかというと、微妙に異なります。
前述の通り、dispatch_once_tはアプリの起動中1回しか起動できないので、一度sharedData_のメモリを解放してしまうともう初期化ができません。
@synchronizedの場合は、例えば同じシングルトンのクラスの中に、

SingletonSample.m
- (void)terminate{
    _sharedData = nil;
}

と書くことで_sharedDataを解放後に、再び初期化することができます。
メモリの解放は関数が終了した後に行われるようですので特に問題は無いようです。
クラス内の変数が多すぎて値を初期値に戻すのが大変な場合は、この手を使うのも有りかと思います。

という認識ですが、間違った内容があれば是非教えてくださいね。