ゆるい感じのプログラムを書きたい。

プログラムの敷居を下げて、多くの人が開発出来るように色々書いていきます!

【iPhoneアプリ】Singletonパターンについて

前回、インスタンスとメモリー管理について
記載致しました。

また、インスタンス時には、「Singleton」と呼ばれるデザインパターンがあります。
こちらもメモリー管理に関わる内容になりますので記載していきます。

今回は、「Singletonパターン」についてです。


通常はイメージのように利用クラス毎にインスタンスオブジェクトを
生成し利用します。

f:id:kassans:20140228152519p:plain

では、変動しない固定変数を取得するクラスを作成した場合どうでしょう


上記のように個々にインスタンスを行うと、インスタンス分の無駄なメモリーが消費されてしまいます。

インスタンス時のメモリー消費については
「【iPhoneアプリインスタンスとメモリー管理について」を参照ください。


このような事象を解消するため、「Singleton」パターンを使い、共有で利用出来る
インスタンスオブジェクトを作成します。

これにより、メモリー消費は押さえれ、かつ別クラスで使い回す事が可能となります。


しかし、デメリットとしてはStaticでオブジェクトを格納する為
不要になっても自動的にメモリー解放はされません。
臨機応変に通常クラスとSingletonパターンクラスを使い分けて頂ければと思います。


イメージとして、このような感じです。

f:id:kassans:20140228152904p:plain


他の利用用途として、こんな場合ではと思っております。

 ・DBコネクションを1カ所で接続数を管理する場合
 ・設定情報など、固定値のみを扱うクラスの場合
 ・クラスの処理が重く、インスタンス変数などメモリーを多く使う場合
 ・ログ登録など、複数インスタンスを行う必要ない場合


では、実際に書いてみましょう

Singleton」をObject-cでプログラムを書くと以下になります。

ヘッダーファイルはこんな感じで書きます。

#import <Foundation/Foundation.h>

@interface SingletonDesign : NSObject{
    @public NSString *object;
}

+ (SingletonDesign *)instanceObject;
- (NSString*)object;

@end


モデルファイルはこんな感じで書きます。

#import "SingletonDesign.h"

@implementation SingletonDesign


//インスタンスオブジェクトを入れる変数
static SingletonDesign *sharedObject = nil;


/*
 SingleDesignのインスタンスを行うメソッド
「+」表記の為、インスタンス無しで指定可能。
*/
+ (SingletonDesign *)instanceObject{
    
 if (!sharedObject) { // A
       //インスタンス実行
       sharedObject = [[SingletonDesign alloc] init];
    }
    return sharedObject;
}

//クラスのメモリ解放時によばれる。
- (void)terminate{
    sharedObject = nil;
}

//イニシャライズ(初期化)
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"イニシャライズ実行");
        object = @"Singleton 変数";
        //Initialization
    }
    return self;
}

//呼び出しテスト用 メソッド
-(NSString*)object{
    NSLog(@"Singleton メソッド実行");
    return object;
}
@end

※ちなみに、プログラム内で「Static」でオブジェクト変数を作成しています。
これは静的領域に確保し「内容を保持し続ける」変数にするためです。


上記、作成後に以下のソースコードで呼び出します。

  NSString* object = [[SingletonDesign instanceObject] object];
  NSLog(@"取得した値:%@",object);

しかし、これでは一つのオブジェクトだけ生成する保証がとれません。


では、1つだけのオブジェクトを保証するには

保証出来ない、原因として
マルチスレッドで同時にオブジェクト生成(instanceObject)を呼ばれる可能性があるからです。


初回のインスタンス
スレッド1インスタンス生成中にスレッド2SingletonDesign.mのAの部分の処理を行い、まだ「オブジェクトは出来てない」と判断され、インスタンスの生成を行ってしまった場合です。



イメージとしてはこのような感じです。

f:id:kassans:20140228154730p:plain


マルチスレッドに対する対応について


上記のような状態を防ぐ為に「@synchronized」を使います。
@synchronized」は、一つのスレッドが処理中は、他の処理を待たせる事が出来ます。


このようなソースの記述をします。

#import "SingletonDesign.h"

@implementation SingletonDesign


//インスタンスオブジェクトを入れる変数
static SingletonDesign *sharedObject = nil;


/*
 SingleDesignのインスタンスを行うメソッド
「+」表記の為、インスタンス無しで指定可能。
*/
+ (SingletonDesign *)instanceObject{
    //以下の処理実行中は、他のスレッドはここで処理待ちとなる。
    @synchronized(self){
        if (!sharedObject) { //A
            //インスタンス実行
            sharedObject = [[SingletonDesign alloc] init];
        }
        return sharedObject;
    }
}

//イニシャライズ(初期化)
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"イニシャライズ実行");
        object = @"Singleton 変数";
        //Initialization
    }
    return self;
}

//呼び出しテスト用 メソッド
-(NSString*)object{
    NSLog(@"Singleton メソッド実行");
    return object;
}
@end

このように記述しインスタンス処理が終了後、同時処理をしてきたスレッド2SingletonDesign.mのA部分で「インスタンス済み」と判断され


これで1つだけのインスタンスを保つことができます。


イメージとしてはこんな感じです。

f:id:kassans:20140228154644p:plain


余談ですが、「@synchronized」の他に「dispatch_once_t」もありますが
これは「初期化処理を1回だけ実行」のため。
インスタンスオブジェクトのメモリー解放をすると再度、初期化出来なくなります。


一応、ソースはこんな感じにかきます。


#import "SingletonDesign.h"

@implementation SingletonDesign


//インスタンスオブジェクトを入れる変数
static SingletonDesign *sharedObject = nil;


/*
 SingleDesignのインスタンスを行うメソッド
「+」表記の為、インスタンス無しで指定可能。
*/
+ (SingletonDesign *)instanceObject{

    //以下の処理は1回だけ実行される。
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
            sharedObject = [[SingletonDesign alloc] init];
		});
    return sharedObject;
}

//イニシャライズ(初期化)
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"イニシャライズ実行");
        object = @"Singleton 変数";
        //Initialization
    }
    return self;
}

//呼び出しテスト用 メソッド
-(NSString*)object{
    NSLog(@"Singleton メソッド実行");
    return object;
}
@end

メモリーを解放後、再度インスタンスするには


また、Singleton生成クラス内に「terminate」を使うと、メモリー解放後
インスタンスを再度行う事が出来ます。


ソースは以下の通りです。

#import "SingletonDesign.h"

@implementation SingletonDesign


//インスタンスオブジェクトを入れる変数
static SingletonDesign *sharedObject = nil;


/*
 SingleDesignのインスタンスを行うメソッド
「+」表記の為、インスタンス無しで指定可能。
*/
+ (SingletonDesign *)instanceObject{
    //以下の処理実行中は、他のスレッドはここで処理待ちとなる。
    @synchronized(self){
        if (!sharedObject) {
            //インスタンス実行
            sharedObject = [[SingletonDesign alloc] init];
        }
        return sharedObject;
    }
}

//クラスのメモリ解放時によばれる。
- (void)terminate{
    sharedObject = nil;
}

//イニシャライズ(初期化)
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"イニシャライズ実行");
        object = @"Singleton 変数";
        //Initialization
    }
    return self;
}

//呼び出しテスト用 メソッド
-(NSString*)object{
    NSLog(@"Singleton メソッド実行");
    return object;
}
@end