Help us understand the problem. What is going on with this article?

iOSのシングルトンの話

More than 5 years have passed since last update.

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

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

yuky_az
「ヒトとAIの共生」がミッションの会社、SAI-Lab株式会社の代表取締役。 東北大学大学院理学研究科修了。理学博士(物理学)。 著書に「はじめてのディープラーニング」、「No.1スクール講師陣による 世界一受けたいiPhoneアプリ開発の授業」。オンライン教育プラットフォームUdemyで、2万人近くを指導する人気講師。
https://sai-lab.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away