2010年6月13日日曜日

Objective-Cでのメッセージングシステム

Objective-Cでのメッセージングシステム 


概要

    Objective-Cのメッセージングのシステムを使い、

    インスタンス間の通信を行うクラスを自作した。

    NSNotification を応用した、メッセージ伝達を行う。


    このクラスのインスタンスをもつオブジェクト同士は、

    お互いのポインタを全く知らない(保持しない)状態での交信が可能になる。


    一言で言うと、アプリ内疑似サーバ立ち上げ機能である。


    通信の順序は、タイマー実行を行わない限り、NSNotificationのシステムにより、

    通信完了まで確実に入力順が保たれる(メッセージの追い越しは発生しない)



    メッセージングする同士のIdentifyを確認するためのキーとして、

    Identifier(メッセージの送信者を特定するためのIDint値)を用意する必要がある。

    セントラル/レシーバーという役割分担を行う必要がある。


    セントラルからのメッセージングは、セントラル自身と指定したレシーバーへと伝達される。

    レシーバーからのメッセージは、セントラルへと伝達される。


    おまけで、下記実行を可能にしてある。

    ・遅延実行(別スレッドでの実行を可能にする)

    ・コマンドの入力(ヘッダクラス参照)

 


ダウンロード

    http://homepage.mac.com/sassembla/download/messagesystem/com.zip



設計思想

    このクラスに自発的に用意されている制限として、セントラルは一つ、レシーバーは一つあるいは

    複数存在する事が想定されている。

    各レシーバーからのメッセージは、必ずセントラルへ集約され、セントラルで状態によって各レシーバー

    (か、セントラル自身)へとメッセージを伝達、処理を進める事が可能。


    →このような区別が存在する理由は、下記2つ。

    ・完全にレシーバー間の通信を行う場合、メッセージ内容を把握する事が不可能になるため。

    ・メッセージの分別によってアプリケーションを複数のモジュールに分解し、

        分業を可能にするための、最低限の仕組み化のため。

 


使用例

    下記のように使用する。



1.初期化:


セントラル.h

//インスタンスを設定する

MessageSystem * messenger_default_reciever;


セントラル.m

//メッセージシステムインスタンスをセントラルとして初期化する。 

messenger_default_observer = [[MessageSystem alloc] initWithCentralObserver:self centralNamed:@"" recieverNamed:@""  withIdentifier:0 withVersion:20100610 withSelector:@selector(defaultCentral:)];



//メッセージを受信したとき行うメソッド、- (void) defaultCentral:(NSNotification * )notification を用意する

- (void) defaultCentral:(NSNotification * )notification {

    NSLog(@"defaultCentral");

    NSMutableArray * array = (NSMutableArray *)[notification userInfo];


    int sender = [[array objectAtIndex:MESSAGE_SENDER] intValue];

    NSLog(@"sender_%d", sender);


    int command = [[array objectAtIndex:MESSAGE_STATEMENT] intValue];

    NSLog(@"command_%d",command);

}




レシーバー.h

//インスタンスを設定する

MessageSystem * messenger_default_reciever;


レシーバー.m

//メッセージシステムインスタンスをレシーバーとして初期化する

messenger_default_reciever = [[MessageSystem alloc] initWithRecieverObserver:self centralNamed:@"" recieverNamed:@""  withIdentifier:1 withVersion:20100610 withSelector:@selector(defaultRecieve:)];


//メッセージを受信したとき行うメソッド、- (voiddefaultRecieve:(NSNotification * )notification を用意する

- (void) defaultRecieve:(NSNotification * )notification {

    NSLog(@"defaultRecieve");

    NSMutableArray * array = (NSMutableArray *)[notification userInfo];


    int sender = [[array objectAtIndex:MESSAGE_SENDER] intValue];

    NSLog(@"sender_%d", sender);


    int command = [[array objectAtIndex:MESSAGE_STATEMENT] intValue];

    NSLog(@"command_%d",command);

}



2.実行

ケースa.
    //セントラル.mのインスタンスにて、下記を実行すると、

    [messenger_default_observer sendMessage:100,nil];


    //セントラル.mのインスタンスにて、defaultCentralメソッドが発動する。
    defaultCentral~

    →セントラルからセントラルへのメッセージ送付にはsendMessageメソッドを使用する。 宛先指定は特にない


ケースb.
    //セントラル.mのインスタンスにて、下記を実行すると、

    [messenger_default_observer flushMessageTo:1 withNil:101,nil];


    //レシーバー.mのインスタンスにて、defaultRecieveメソッドが発動する。
    defaultRecieve~

    →セントラルからレシーバーへのメッセージ送付にはflushMessageToメソッドを使用する。 宛先指定は1の部分。


ケースc.
    //レシーバー.mのインスタンスにて、下記を実行すると、

    [messenger_default_reciever sendMessage:102,nil];


    //セントラル.mのインスタンスにて、defaultCentralメソッドが発動する。

    defaultCentral~

    →レシーバーからセントラルへのメッセージ送付にはflushMessageToメソッドを使用する。 宛先指定は特にない



    MessageSystemのインスタンス同士に、クラス間の関連や、ポインタの共有が一切無い、ということに注目。
    なのに通信内容がお互いのクラスに届いている。素敵すぎる。



そのほか
    ・NSオブジェクトを送る事が可能
    sendMessage,flushMessageToメソッドともに、可変長でNSObjectを格納、発送できるようにしている。
    [messenger_default_observer flushMessageTo:1 withNil:101, @"NSオブジェクトを継承したものなら、何でも送れる!", nil];
    NSNumberやNSDictionaryも送れる。


    ・今のところ、単一の識別子しか使えない
    セントラルとレシーバーは、このサンプルでは単一の組み合わせとして動いている。
    セントラル一つ、レシーバ数は無制限だが、この組(セントラル+レシーバーxN)としてしか使えず、
    ひとつこの組み合わせを作ったら、別のセントラル+レシーバーの組み合わせを使用する事が出来ない。
    可能にするには、独自の名前でセントラル+レシーバーの組を実装できるように、関係性アイデンティファイの機能が必要。
    →centralNamed:@""などがあるのは、その実装前の準備。


    ・Deallocが非常に難しい
    MessageSystemを持ったインスタンスをDeallocする際は、先にMessageSystemインスタンスをDeallocする必要がある。
    遅延実行と絡むと、分解が非常に難しくなる。
    また、MessageSystemを介した、MessageSystemインスタンスを持つオブジェクトのDeallocが可能。(なんて恐ろしい)


    ・ポインタを管理する必要が無いようにできたりできなかったり
    Object * a = [[Object alloc] initWithMessageSystem];//Objectクラスは初期化時にMessageSystemインスタンスを初期化
    と書くと、
    記述したブロックを抜けても、MessageSystemを介してaにアクセスする事が出来る。
    ぶっちゃけメモリリークだが、MessageSystemを介して管理が出来るのが強み。
    Deallocをマスターすれば、いろいろなオブジェクトを、MessageSystemを介したシングルトンとして
    意識せず実行する事が出来る。


今後の展望
    要望、バグがあれば、是非ブログまで連絡ください。
    
    そのうちGoogleCodeにアップしようかな。


0 件のコメント:

コメントを投稿

フォロワー