Swanman's Horizon

性欲をもてあましつつなんらかの話をするよ。

TMessageManagerってこんなの。

あっとうてきなどきゅめんとぶそく\(^o^)/

TMessageManagerとはなんぞや

TMessageManagerは、FMX.Messagesユニット内にある200行足らずの小さなクラスで、FireMonkey内における簡単なメッセージング機構を提供しているようです(推測)

使い方は簡単で、

  1. TMessageクラスを継承したメッセージクラスを作る
  2. TMessageManager.DefaultManager.SubscribeToMessageでハンドラを登録
  3. TMessageManager.DefaultManager.SendMessageでメッセージを送る

といった感じです。

1.TMessageを継承したメッセージクラスを作る

TMessage自体は「TMessage = class abstract end;」だけの中身の無いクラスです。
単純にタイミングだけを伝えたい場合は名前だけ別にした継承クラスを作ればいいですが、イベントに付随する情報も伝えたい場合は、継承したクラスの中にプロパティ等を持たせることで可能です。

2.TMessageManager.DefaultManager.SubscribeToMessageでハンドラを登録

SubscribeToMessageメソッドで、メッセージが送られた時に処理を実行するためのハンドラを登録します。このメソッドには2つのオーバーロードがあり、普通のメソッドと無名メソッドが渡せるようになってます。

// 無名メソッド版
function SubscribeToMessage(
    const AMessageClass : TClass;
    const AListener : TMessageListener) : Integer; overload;
// 通常のメソッド版
function SubscribeToMessage(
    const AMessageClass : TClass;
    const AListenerMethod : TMessageListenerMethod) : Integer; overload;

AMessageClassには1で作ったメッセージクラスをそのまま(インスタンス化せず)渡します。一方、ハンドラにはそのクラスのインスタンスが渡されますが、型がTMessageなのでキャストして使います。正直ジェネリクスがあるんだからTMessage固定にする必要はなかった気もしますが、まぁそれはそれで…。

ちなみにハンドラは複数登録でき、Unsubscribeメソッドで解除も可能です。登録が2通りあるのに対し、解除は3通りのメソッドがあって、メッセージクラスを渡すところまでは全部一緒ですが、もうひとつの引数が、メソッド、無名メソッド、あるいはIDとなってます。このIDはSubscribeToMessageが返すInteger値で、特に無名メソッドを登録した時は(いちいち無名メソッドを変数に入れて保持しておくよりも)IDだけ保持しておいた方が良さそうです。

3.TMessageManager.DefaultManager.SendMessageでメッセージを送る

今度は呼ぶ方です。SendMessageも2つのオーバーロードを持ちます。

procedure SendMessage(const Sender: TObject; AMessage : TMessage); overload;
procedure SendMessage(const Sender: TObject; AMessage : TMessage; ADispose : Boolean); overload;

ハンドラの登録時はクラスをそのまま渡したのに対し、SendMessageではインスタンスを渡します。実行される処理的にはどちらもほとんど一緒ですが、ADisposeをTrueにするとSendMessage内でAMessageを破棄してくれます。Falseにした場合は全く同じです。

SendMessageの中ではSubscribeToMessageで登録されたハンドラのうち、AMessageのクラスに紐付けられたものを順番に呼び出します。登録されたハンドラはTDictionary内に保持されるため、登録した順番で呼び出されるという保証はないことに注意して下さい。

で、これが何の役に立つの?

実際これだけでは今まで色んな人が似たような機構を散々作ってきたので新鮮味がないですが、これの利点はFireMonkey内に元々組み込まれているという点にあります。

特に真価を発揮しそうなのはアイドル処理です。Application.OnIdleは元のApplicationがシングルトンなため、アプリケーション内の複数の処理がOnIdleを使おうと思った時、OnIdleを都度差し替えるか、別途処理をストックして、OnIdle内で順次実行するといった仕組みが必要になります。

ところがこのOnIdle、実は内部でTIdleMessage*1をSendMessageしてくれています(正確にはDoIdle内)。ということは、TIdleMessageに対してSubscribeToMessageをすれば、簡単にOnIdleのタイミングでの処理がアプリケーションのあちこちで書けると言うことです。これでFireMonkeyでアプリを作る時、もうApplication.OnIdleを直接使う必要はなさそうかも?

以下は簡単なサンプルです。

type
  TForm1 = class(TForm)
    ...
    FAnonHandlerId: Integer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure AppIdle(const Sender: TObject; const M: TMessage);
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // ハンドラの登録
  TMessageManager.DefaultManager.SubscribeToMessage(TIdleMessage, AppIdle);
  FAnonHandlerId := TMessageManager.DefaultManager.SubscribeToMessage(TIdleMessage,
    procedure(const Sender: TObject; const M: TMessage)
    begin
      ...
    end);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // ハンドラの解除
  TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, AppIdle);
  TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, FAnonHandlerId);
end;

*1:FMX.Types内で定義