月別アーカイブ: 2013年8月

NSNotificationCenterを使った通知のサンプル

オブジェクト間で通信、通知を行いたい事って頻繁にありますよね。
解決方法として色んなパターンがあると思いますが、cocoaフレームワークには大変便利な通知システムが用意されています。

使い方も簡単で、しかもオブジェクト間の関連性が薄いので大変使いやすいです。

通知は文字列の名称を使って識別されますので、ヘッダに定数を宣言しておきます。
通知を発行するクラスのヘッダに書くのが良いのではないでしょうか?

#define kDataManagerFinishLoading @"DataManagerFinishLoadingMsgKey"

通知を発行する側はこんな感じで簡単に通知できます。

// 引き渡しパラメータの作成
// Dictionaryでなんでも渡せます
NSDictionary* info = @{ 
  @"date":[NSDate date],
  @"title":@"目くじら", 
  @"count":[NSNumber numberWithInt:12] 
};

// 通知                    
[[NSNotificationCenter defaultCenter] 
    postNotificationName:kDataManagerFinishLoading  
    object:self 
    userInfo:info];

通知を受け取る側のコードはこんな感じです

最初に、NSNotificationCenter に通知の監視を登録します。
self.dataManagerってのが通知を発行するオブジェクトで、通知を受け取るオブジェクトがselfだとすると。

[[NSNotificationCenter defaultCenter] 
    addObserver:self 
    selector:@selector(handleDataManagerFinishLoading:) //←通知を受け取るセレクタ
    name:kDataManagerFinishLoading 
    object:self.dataManager];

受け取った時の処理を書いておきます

// kDataManagerFinishLoading の通知ハンドラ
-(void)handleDataManagerFinishLoading:(NSNotification *)aNotification
{
    //userInfoはDictionaryなので色んな情報を受け取る事ができます。   
    NSDate* date = [[aNotification userInfo] objectForKey:@"date"];
    NSString* title = [[aNotification userInfo] objectForKey:@"title"];
    NSNumber* count = [[aNotification userInfo] objectForKey:@"count"];
    
    //なにかしら処理

}

通知が不要になったら解除しておきます。
dealloc等に書いておくのがよいのではないでしょうか?

[[NSNotificationCenter defaultCenter] removeObserver:self];

以上

オブジェクトのプロパティを監視する

あるオブジェクトのプロパティ値が変更された時に何かしら処理を行いたい場合のサンプルです。

対象オブジェクト objA が title というプロパティを持っている場合を例とすると下記のような感じです。

objAにプロパティの変更通知を登録 (objAを生成したタイミングなどで)

// 
[objA addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];

通知はaddObserverで登録したオブジェクトの下記のメソッドが呼び出されます

// 監視対象が変更された時に呼び出されるコールバック
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object 
			change:(NSDictionary *)change 
		       context:(void *)context
{
    if ([keyPath isEqual:@"title"])
    {
	NSString* newTitle = [[change objectForKey:NSKeyValueChangeNewKey] stringValue];
        NSLog(@"newTitle :%@", newTitle);

        //変更された時の処理を書く

    }
}

通知が不要となったら登録を解除 (objAを廃棄する前に実行します)

//
[objA removeObserver:self forKeyPath:@"title" context:NULL];

cocoaフレームワーク、、便利過ぎる。。

GCDを使って並列処理

Grand Central Dispatch(GCD)を使って並列処理を行うサンプルです。

ポイント
・dispatch_group_tを作成
・dispatch_queue_tの廃棄を行う為にdispatch_group_notifyを仕込む
・UIの変更等はメインスレッドで実行
・共通変数へのアクセスは @synchronized を使ってロックする
・dispatch_group_asyncのブロック内に autoreleasepool を作る

//GCDで複数のジョブを並列で実行してみるサンプル
-(void)gcdTest1
{
    dispatch_queue_t queue_main = dispatch_get_main_queue();
    dispatch_queue_t queue_job = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
  
    NSObject* syncRoot = [[NSObject new]autorelease];//スレッド間の同期用
    __block int counter = 0;
  
    for (int i=0; i<5; i++) {
   
        dispatch_group_async(group, queue_job, ^{
          @autoreleasepool{
              if (i==0)
              {
                  //グループの終了処理を登録しておく
                  //ここではqueue_mainを渡していますが、mainでこのスレッドを待機しているような状況では
                  //デッドロックが起こりますので、その場合はqueue_jobを渡して下さい。
                  dispatch_group_notify(group, queue_main, ^{
                      //dispatch_group_tをリリース
                      dispatch_release(group);
                  
                      //なにか処理を実行(メインスレッドで実行される)
                      NSLog(@"finish group job counter = %d",counter);
                  
                  });
              }
          
              //(ジョブを実行する)
          
              @synchronized(syncRoot){ //共通変数などへのアクセスは@synchronizedを使ってロックする
                  counter += i;
              }
          }
        });
    }
  
    //終了を待機する時は下記のコードで待ちます
    //待機させる時は dispatch_group_notify でリリースさせる必要はなく、
    // ※1でリリースすればOK
    //NSLog(@"waiting...");
    //dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //dispatch_release(group); //※1
}

上記ではdispatch_group_asyncを複数回発行して並列処理をさせていますが、単純にforを置き換えるような使い方であれば dispatch_apply を使ったら簡単に並列処理が可能です。
ただ、dispatch_applyは処理をロックしてしまいますので、完全に非同期で実行する場合には dispatch_async、dispatch_group_asyncのブロック内でdispatch_applyを実行する事が必要です。