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

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

【iPhoneアプリ】これを使えるようにならないと「マルチスレッド」について 実装編

今回も、「マルチスレッド」についてです。

前回、概要編との事で記載していましたが

今回は実際にプログラムを書いて、動かしてみる実装編を記載して行きます。

GCD実用例の前準備

GDCを用いた実用例の紹介の前に以下の準備をします。


 ・スレッドテスト開始用ボタンの描画
 ・スレッドテスト開始用ボタンのアクション処理


まず、始めに「スレッドテスト開始用ボタン」を以下のコードを
記述して描画させます。

■ViewController.m

#import "ViewController.h"
@implementation ViewController{
    MultiThread *Multi;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

  Multi = [[MultiThread alloc] init];
    UIButton* bt  = [Multi ThreadButton];
    
    bt.center = self.view.center;
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self.view addSubview:bt];    
}


次に「スレッドテスト開始用ボタン」のアクション処理を
以下のコードを記述して行います。

■MultiThread.h

#import <Foundation/Foundation.h>

@interface MultiThread : NSObject{
    UIButton* bt;
}

- (UIButton*)ThreadButton;

@end

■MultiThread.m

#import "MultiThread.h"

@implementation MultiThread

- (UIButton*)ThreadButton{
    bt = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [bt setTitle:@"テスト" forState:UIControlStateNormal];
    bt.frame = CGRectMake(0,0,200,200);
    [bt addTarget:self action:@selector(thread_Button:) forControlEvents:UIControlEventTouchUpInside];
    return bt;
}

- (IBAction)thread_Button:(UIButton*)sender
{
 //スレッドの処理をこの部分に記載します。
}

@end

GCDの実用例

実用例として以下の項目を記載します。

 ・並列なキューで同期処理(dispatch_sync)
 ・並列なキューで非同期処理(dispatch_async)
 ・直列なキューで同期処理(dispatch_sync)
 ・直列なきユーで非同期処理(dispatch_async)


■並列なキューで同期処理(dispatch_sync)

・並列なキューの為、実行完了する順番は保証されない。
・dispatch_syncの為、別スレッド処理が完了するまで、次のスレッドは処理しない。
・dispatch_syncの為、全体のキューの処理完了を待って、次の処理を行います。


プログラムはこんな感じです。

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
 for (NSInteger i = 0 ; i < 100; ++i) {
   dispatch_sync(globalQueue, ^{
     NSLog(@"%d", i);
   });
 }

 NSLog(@"here!!");
}


結果はこんな感じです。

labo[6961:70b] 96
labo[6961:70b] 97
labo[6961:70b] 98
labo[6961:70b] 99
labo[6961:70b] here!!

追加したタスクが実行完了するまで次に進まない為、"here!!"は最後に出力されます。


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

f:id:kassans:20140314125012p:plain


また、並列なのに順番に処理をしているイメージはこんな感じです。

f:id:kassans:20140314125442p:plain

▼使いどころ
 ・グローバルキューで複数のスレッドで順番通りにタスクを処理させ
  後ほど、処理を行ったタスクをメインキューで使う必要がある場合に使います。


■並列なキューで非同期処理(dispatch_async)

・並列なキューの為、実行の順番は保証されません。
・dispatch_asyncの為、別スレッド完了を待たず、スレッドは処理します。
・dispatch_asyncの為、全体のキューの処理完了を待たず、次の処理を行います。

プログラムはこんな感じです。

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
 for (NSInteger i = 0 ; i < 100; ++i) {
   dispatch_async(globalQueue, ^{
     NSLog(@"%d", i);
   });
 }

 NSLog(@"here!!");
}


結果はこんな感じです。

labo[6976:70b] here!!
labo[6976:1303] 0
labo[6976:3807] 1
labo[6976:3a03] 2
labo[6976:3b03] 3

順番に実行され"here!!"が全てのタスクの完了より前に来ています。


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

f:id:kassans:20140314124553p:plain

▼使いどころ
 ・あるタスクで処理に時間もかかり、その後の処理でこのタスクの結果を
  用いないといったケースで利用します。


■直列なキューで同期処理(dispatch_sync)

 ・直列なキューの為、タスクが同時に一つしか実行しないので競合が起きない。
 ・dispatch_syncの為、全体のキューの終了完了まで次のステップに進みません。

プログラムはこんな感じです。

dispatch_queue_t privateQueue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_SERIAL);

 for (NSInteger i = 0 ; i < 100; ++i) {
   dispatch_sync(globalQueue, ^{
     NSLog(@"%d", i);
   });
 }

 NSLog(@"here!!");
}


結果はこんな感じです。

labo[1812:70b] 96
labo[1812:70b] 97
labo[1812:70b] 98
labo[1812:70b] 99
labo[1812:70b] here!!

出力は順番になっており、追加したタスクが実行完了するまで次に進まない為、
"here!!"は最後に出力されています。


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

f:id:kassans:20140314125114p:plain

▼使いどころ
 ・ファイルはDBなど不整合が起きうる箇所で、かつその読み込んだ内容を
  次で使う場合に使います


■直列なキューで非同期処理(dispatch_async)

 ・直列なキューの為、タスクが同時に一つしか実行しないので順番が前後しません。
 ・dispatch_asyncの為、全体のキューの処理完了を待たず、次の処理を行います。


プログラムはこんな感じです。

dispatch_queue_t privateQueue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_SERIAL);

 for (NSInteger i = 0 ; i < 100; ++i) {
   dispatch_async(globalQueue, ^{
     NSLog(@"%d", i);
   });
 }

 NSLog(@"here!!");
}


結果はこんな感じです。

labo[1789:70b] here!!
labo[1789:1303] 0
labo[1789:1303] 1
labo[1789:1303] 2
labo[1789:1303] 3

出力は順番になっており、"here!!"が先に表示され、タスクを追加し終わると
次に進んでます。


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

f:id:kassans:20140314124448p:plain

▼使いどころ
 ・処理内容に一意性が求められる時に使います。
 ・例えばファイルへのアクセスなどが該当します。
 ・ファイルへのアクセスなどは時間がかかるのでasyncで非同期実行する
  といった使い方ができます。

メインスレッドとグローバルスレッドの組み合わせ方

次に、メインスレッドグローバルスレッドの組み合わせを紹介していきます。


以下のように処理分散させたい場合

・メインスレッドで描画処理
・グローバルスレッドで別処理


こんな感じのプログラムを書いてみます。

dispatch_queue_t main = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_async(globalQueue, ^{
        
    int count = 0, i;
    
		//描画に必要な処理1
		for(i = 0; i < 5; i++){
        NSLog(@"HeavyProcessing_1 = %d", count);
        count++;
    }
    
    //メインスレッドで途中を描画
    dispatch_async(main, ^{
         NSLog(@"Main_1 = %d", count);
    });
    
    //描画に必要な処理2
    for(i = 0; i < 5; i++){
        NSLog(@"HeavyProcessing_2 = %d", count);
        count++;
    }
    
    //メインスレッドで描画完了
    dispatch_async(main, ^{
         NSLog(@"Main_2 = %d", count);
    });

});
	 //通常の処理
   NSLog(@"here!!");


結果はこんな感じです。

labo[1969:70b] here!!
labo[1969:1303] HeavyProcessing_1 = 0
labo[1969:1303] HeavyProcessing_1 = 1
labo[1969:1303] HeavyProcessing_1 = 2
labo[1969:1303] HeavyProcessing_1 = 3
labo[1969:1303] HeavyProcessing_1 = 4
labo[1969:70b] Main_1 = 5
labo[1969:1303] HeavyProcessing_2 = 5
labo[1969:1303] HeavyProcessing_2 = 6
labo[1969:1303] HeavyProcessing_2 = 7
labo[1969:1303] HeavyProcessing_2 = 8
labo[1969:1303] HeavyProcessing_2 = 9
labo[1969:70b] Main_2 = 10

グローバルスレッドがasync(非同期処理)のため、先に通常処理が行われ
次にグローバルスレッドの処理が行われています。

グローバルキューでは、重い処理を行った後にメインスレッド
呼ばれている事が分かります。



このため、グローバルスレッド「重たい処理」メインスレッド「描画処理」
分散処理の実装が可能となります。

【iPhoneアプリ】これを使えるようにならないと「マルチスレッド」について 概要編

今回は、「マルチスレッド」についてです。

iOSの開発を行うにあたり、必要不可欠だと思い記載します。
今回は概要編実装編の2部構成で記載します。

また、mixiのiOSTraningのサイトを参考にして
自分自身が見返した際に分かるような形に編集しています。

■参考サイト
https://github.com/mixi-inc/iOSTraining/wiki/8.2-Grand-Central-Dispatch

iOSのマルチスレッドについて

マルチスレッドを行うには以下の方法があります。

NSThread
  ・スレッドを立てて、そのスレッドの中で処理を行う。
  ・スレッドの管理やキューイングなどの管理はアプリケーションが行う

GCD (dispatch_xxx)
  ・スレッドの管理などをOSレベルで実装。
  ・処理をしたいタスクをBlockで渡す。
  ・渡されたタスクはキューに挿入されて逐次実行される。


最近は、GCDがよくサイトに記載されています。
NSThreadだとスレッドの処理など、自前で書かないといけないのでコードが冗長
なってしまい大変です。


そこで、iOSの並列処理実行技術のGCDについて記載して行きます。

並列処理の必要性

まず、GCDの紹介の前に、並列の処理の必要性について記載します。


以下のケースの場合に並列処理が必要となります。

・通信処理に時間が掛かってしまう場合。
・ファイルへのアクセス、DBへのアクセスで時間がかかってしまう場合。


上記の処理を、メインスレッド(画面描画スレッド)で行った場合。
画面描画やユーザーのアクションへの反応ができなくなり、"固まった"状態になります。


また、iPhone5以降ではApple A6プロセッサでコア2つ搭載していますが
並列処理を行わないと、コア1つを持て余す事になります。

その事から、待ち時間の発生する処理計算に時間のかかる処理などは
複数のスレッドを用いて並列に処理を行うことでCPUのパワーを持て余さず
使用する事ができます。

GCDの紹介

iOSでは、並列処理を簡単に行う事が出来る仕組みとして、GCDがあります。
これは、iOS4以降で利用可能です。


では、紹介して行きます。


■dispatch queueとタスクについて

dispatch queue(ディスパッチキュー)と呼ばれる入れ物にタスクを追加
dispatch queue(ディスパッチキュー)に追加されたタスクは、FIFOで順番に実行
タスクの実行は、メインスレッドとは別のスレッドで実行
タスクはBlocksとして登録する


その他、このような特徴があります。

■管理するキューは以下の種類があります。
 ・アプリケーション独自に生成(プライベートキュー)
 ・アプリケーション起動時にシステム内で自動的に生成(グローバルキュー)

■OSが以下の管理を自動的に行います。
 ・適切なスレッドへのタスクの振り分け
 ・アプリケーション内部で自動生成されたキュー
 ・キューに追加されたタスクを実行するスレッド


イメージはこんな感じです。
f:id:kassans:20140313125458p:plain


dispatch queueの種類

▼メインディスパッチキュー(メインキュー)
 ・アプリケーションのどこからでも利用できる直列(シリアルキュー)
 ・アプリケーションのメインスレッドで実行
 ・UIの描画が更新はこのキューで行う必要がある。


▼直列SerialDispatchQueue(直列なキュー)
 ・タスクを同時に一つずつ追加された順に実行する仕組み
 ・タスクの実行が終わると次のタスクの実行を行う。
 ・タスクは他のキューと独立したスレッド上で動作。


■SerialDispatchQueueのイメージ

f:id:kassans:20140313125502p:plain


▼並列ConcurrentDispatchQueue(並列なキュー)
 ・複数のタスクを同時に実行する。
 ・実行の順番はキューに追加した順番になるが、終了のタイミングの順序は
  保証されない。
 ・同時に実行するタスクの数はシステムの状況に応じて変化する。
 ・アプリケーションが所有するglobal queueとアプリケーション内部で生成する
  queueを持てる。
 ・キューには優先度をつけることができる 


■ConcurrentDispatchQueueのイメージ

f:id:kassans:20140313125505p:plain

dipatch queueへのタスク処理追加

dipatch queueのタスク追加は以下の関数を使います。

■dispatch_async

 ▼特徴
  ・blockで定義された処理をqueueに追加する。
  ・非同期処理でタスクの実行完了を待たず、次の制御に進む。

 ▼記述方法

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)


■dispatch_sync

 ▼特徴
  ・blockで定義された処理をqueueに追加する。
  ・同期処理でタスクの実行が完了するまで制御は止まる。

 ▼記述方法

void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)


引数の説明

queue
 処理をキューイングしたいキューを指定
block
 処理したい内容を記述したブロック


プログラムで書くとこんな感じです。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    NSLog(@"Hello GCD!");
});

キューの生成と取得

■メインキュー
 ・メインディスパッチキューを取得して利用する
 ・直列(シリアル)キュー

▼取得方法

dispatch_queue_t dispatch_get_main_queue(void);


■グローバルキュー
 ・優先度が異なる、4つのグローバルキューが生成されているので、
  アプリケーションのどこからでも取得し利用する。
 ・並列(コンカレント)キュー

▼取得方法

dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);


priorityの設定
以下のいずれかのキーを指定

キー名 優先度
DISPATCH_QUEUE_PRIORITY_HIGH 優先度高
DISPATCH_QUEUE_PRIORITY_DEFAULT 優先度中
DISPATCH_QUEUE_PRIORITY_LOW 優先度低
DISPATCH_QUEUE_PRIORITY_BACKGROUND 優先度バックグラウンド

flagsの設定
将来の拡張に備えて予約されています。当面は常に0を渡すようにしてください。


■プライベートキュー
 ・アプリケーションの一部のみで利用されるキューを生成して利用する事も可能です。
 ・直列(シリアル)キュー、並列(コンカレント)キューどちらも可能

▼生成方法

dispatch_queue_t dispatch_queue_create( const char *label, dispatch_queue_attr_t attr);


labelの設定
キューのラベルを付けます。逆DNS記法でつけることが推奨されています。
デバッグを行った時のスタックトレースなどに出力されます。

dispatch_queue_attr_tの設定
キューのタイプが直列か並列を指定します。(iOS5以降対応)

キュータイプ キー名
直列 DISPATCH_QUEUE_SERIAL
並列 DISPATCH_QUEUE_CONCURRENT


まとめ

グローバルキュー、メインキューでは
 タスクをエンキューしても実際にどの順番で実行されるか不明

プライベートなキューでは
 必ずキューの先頭からタスクを実行していくのでシリアルなキューを実現


しかし、プライベートなキューを作成すると
Retainカウントが1なので 最後にリリースをすることを忘れずに


▼以下のコードでリリース

dispatch_release(high);


次回は、実装編でプログラムでの記述方法などを記載します。

【iPhoneアプリ】基本を忘れないように「オブジェクト指向」について

今回は「オブジェクト指向」についてです。


今後、「Objectiv-C」や「Java」などの開発を行うにあたり
忘れては行けないと思いましたので記載します。

オブジェクト指向とは

プログラム作成する処理内容を物(オブジェクト)として捉え
属性」と「機能」に分類して表現する考え方です。


では、実際にオブジェクト指向の例を使って記載して行きます。


自動販売機のシステムオブジェクト指向を考えてみます。

まず、性質を整理します。
下記の絵のように洗い出してみます。

f:id:kassans:20140307194002p:plain


次に、「属性」「機能」に分けてみます。

属性とは
オブジェクトが持っている値を指します。
機能とは
オブジェクトの値を操作する事を指します。

f:id:kassans:20140307185043p:plain


このように整理を行うと
以下の特徴がある事が見えてきます。

・オブジェクト生成時に以下の項目の値を決める必要がある。
 商品名、商品の価格

・オブジェクト生成後に以下の項目の値が決まる。
 投入金額

・属性・操作ともに洗い出しの時点では抽象的な言葉で表現してある
 例:どんな商品名なのか不明
   どんな商品を出すのか不明

など

特徴を整理し設計を行う

これらを以下のようにまとめ(設計し)てみます。


▼初期設定について(コンストラクタまたは、イニシャライズ)
・「オブジェクト生成時に以下の項目の値を決める必要がある。」
 とのことで属性の「商品名」「商品の価格」を初期設定する。

▼機能について
・機能「お金を入れる」を利用すれば、属性の「投入金額」を設定出来る。
・機能「商品を出す」を利用すれば、初期設定した属性の「商品名」が出てくる。
・機能「おつりを出す」を利用すれば、初期設定した属性の「商品の価格」と「投入金額」の差額を出す。

上記の記載内容を見ると機能によって、属性の値が変換している事が分かります。

■実際にプログラムで書くと以下のようになります。


ヘッダーファイルはこんな感じです。
■Vending.h

#import <Foundation/Foundation.h>

@interface Vending : NSObject{
    
    //商品名
    @private NSString *itemName;
    //商品の金額
    @private int itemPrice;
    //投入金額
    @public int itemPay;
    
}
//イニシャライザ
-(id)initWithVending:(NSString *)name price:(int)price;

//メソッド名:お金を入れる
-(void)setCoin:(int)coin;
//メソッド名:おつりを出す
-(int)getCoin;
//メソッド名:商品を出す
-(NSString*)getItem;

@end


モデルファイルはこんな感じです。
■Vending.m

#import "Vending.h"

@implementation Vending

/**
 * イニシャライザ(初期化)
 */
-(id)initWithVending:(NSString *)name price:(int)price{
    if((self = [super init]))
    {
      //商品名
      itemName = name;
      //商品の価格
      itemPrice = price;
    }
    return self;
}


/**
 *  メソッド名:お金を入れる
 *  用途:引数で受け取った、「投入金額」を属性「投入金額」に代入
 *
 * @param coin 投入金額
 *
 */
-(void)setCoin:(int)coin{
    itemPay = coin;
}

/**
 *  メソッド名:おつりを出す
 *  用途:「投入金額」と「商品の価格」差額を返す。
 *
 * @return おつり
 *
 */
-(int)getCoin{
  return itemPay-itemPrice;
}

/**
 *  メソッド名:商品を出す
 *  用途:「商品名」を返す。
 *
 * @return 商品名
 *
 */
-(NSString*)getItem{
    return itemName;
}


@end


メインファイルをこんな感じで書いて、実行します。
■ViewController.m

    Vending *Ven = [[Vending alloc] initWithVending:@"コーラ" price:100];
    [Ven setCoin:200];
    NSLog(@"商品名;%@",[Ven getItem]);
    NSLog(@"おつり:%d",[Ven getCoin]);

なぜ、オブジェクト指向を使うのか

使う理由として、以下のメリットがあるからです。

オブジェクト指向のメリット


▼保守性の向上
クラス化を行うことにより、設計図が確立されており
以下の2点のメリットがあります。

 ・障害発生時に的確に問題を把握し対応出来る。
 ・仕様変更時に的確に場所を特定出来、対応出来る。

▼再利用性(汎用性)
クラス名「自動販売機」属生名「商品名」など
あいまいな命名をしています。

なので、オブジェクト生成時に以下のように、販売機を変えることが
可能となります。

f:id:kassans:20140307185042p:plain


上記のメリットがあり、汎用性も高く使い回せるようになるため
オブジェクト指向を使うメリットがあります。

【iPhoneアプリ】init(イニシャライザ)を用いた初期化について

今回は「init(イニシャライザ)」についてです。

Javaを経験されている方なら、気づいていると思いますが
通常、インスタンスを行う際に「コンストラクタ」という初期化処理を行います。
その部分から記載させて頂きます。

コンストラクタについて

コンストラクタとは、インスタンス時に必ず初回に処理されるメソッドの事です。


通常は、以下のようにコンストラクタを作成し、
中身は何も書かないです。

public class Object {
  private int itemA;
  public  int itemB;

  //コンストラクタ
  Object () {
     //空のコンストラクタ
  }
  //クラス名と同じ名前のメソッドを作成します。
}


ですが、インスタンス変数などクラスを利用する前に
初期化処理が必要な場合は、以下のように処理を記載します。

public class Object {
  private String itemA;
  public  String itemB;

  //コンストラクタ
  Object () {
		itemA = "初期化A";
		itemB = "初期化B";
  }
	//クラス名と同じ名前のメソッドを作成します。
}


また、コンストラクタに引数を追加し、引数の値で初期化することも可能です。

public class Object {
  private String itemA;
	public  String itemB

  //コンストラクタ
  Object (String A, String B) {
		itemA = A;
		itemB = B;
  }
	//クラス名と同じ名前のメソッドを作成します。
}

では、Object-cではどうでしょう。

Objectiv-cの初期化について

このようにJavaでは、コンストラクタを用いて初期化を行うのですが
Objectiv-cにはコンストラクターがありません


その代わりに「init」イニシャライザがあります。
インスタンス時に記載しているコレです。

Object obj = [[Object alloc] init];

この宣言を行う事により、コンストラクタと同じ初期化を
行う事が出来ます。


クラスの実装例は以下の通りです。

モデルファイル
■Initialize.m

//イニシャライザ
- (id)init {
    if((self = [super init]))
    {
        NSLog(@"初期化処理");
    }
    return self;
}

その他利用方法について

オーバライドを使うとにより、引数ありの初期化も可能となり
複数のイニシャライザもオーバーライドできます。


以下のソースが主な使い方になります。


ヘッダーファイル
■Initialize.h

@interface Initializer : NSObject

//オリジナルのイニシャライザの定義
-(id)initWithTitle:(NSString *)aTitle date:(NSDate *)aDate;
-(id)initWithTitle:(NSString *)aTitle;

@end


また、以下のソースのように、引数をすべて明示的に指定する「initWithTitle」は
クラスで最も重要なイニシャライザで「指定イニシャライザ」といいます。
Apple Developer CoCoaEncyclopedia.pdfより


モデルファイル
■Initialize.m

#import "Initializer.h"

@implementation Initializer

//指定イニシャライザ
-(id)initWithTitle:(NSString *)aTitle date:(NSDate *)aDate{
    if((self = [super init]))
    {
        NSLog(@"タイトル:%@ 日時:%@",aTitle,aDate);
    }
    return self;
}


//副次的イニシャライザ(引数あり)
- (id)initWithTitle:(NSString *)aTitle {
    return [self initWithTitle:aTitle date:[NSDate date]];
}

//副次的イニシャライザ(引数なし)
- (id)init {
    return [self initWithTitle:@"引数なし"];
}

@end


このように設定を行ったイニシャライザを以下のプログラムで実行します。


メインファイル
■ViewController.m

 //指定イニシャライザ
 Initializer  *Init1 = [[Initializer alloc] init];
 //副次的イニシャライザ(引数あり)
 Initializer  *Init2 = [[Initializer alloc] initWithTitle:@"引数あり"];
 //副次的イニシャライザ(引数なし)
 Initializer  *Init3 = [[Initializer alloc] initWithTitle:@"引数あり2個" date:[NSDate date]];

また、これらはNSObjectを基準としたイニシャライザの為
他のクラスのイニシャライザはAppleのdeveloperをご確認ください。

https://developer.apple.com/jp/devcenter/ios/library/japanese.html

【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);

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

続きを読む