MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第110号

2004-04-21

目次

  • Cocoaでいこう! Macらしく  第47回  Yoshiki(DreamField)
  • 小池邦人の「Carbon API 徒然草」
  • 「Behind the WebObjects」  第18回  田畑 英和
  • ニュース・解説
  • MOSAからのお知らせ

Cocoaでいこう! Macらしく  第47回 Yoshiki(DreamField)

パネルを表示しよう(その9)

 前回は、ImageInfoにchangeメソッドを実装しました。これで、ImageInfoにchangeメッセージを投げることにより、ImageInfo自身が「ImageInfoが変わったぞ」という通知を行ってくれます。それでは、MyDocument.mの中で、ImageInfoを変更した後に、changeメッセージを投げる処理を加えましょう。

- (void)windowDidBecomeMain:(NSNotification *)aNotification {
    ImageInfo *imageInfo;

    imageInfo = [ ImageInfo sharedImageInfo];
    [ imageInfo setImageSize:[ image size]];
    [ imageInfo change];
}


 付け加えたのは、下から2行目です。ところで、何でimageInfoにsizeをセットした時に、自動的に通知を行うようにしなかったのでしょうか。その方がオブジェクト指向として正しい気がします。確かに、今回の場合は、sizeしかセットしていませんから、その時点で通知してしまってかまいません。ですが将来、size以外にも色々とセットするようになった時に、それぞれの値をセットするたびに通知されたら、プログラムの効率が悪くなってしまいます。そこで、今回はこういう実装としました。なお、この方法が正解というわけではありません。これ以外に、例えば、通常は値をセットした時点で通知が行われるが、これを連続で行っている間抑止できる機能を付けるという方法も考えられます。その他にも色々な方法があるでしょう。この辺り、どう実装するかは、個人の趣味の範疇になりますので、色々とやり方を考えてみて下さい。こういったロジックを考えるのも、プログラムの楽しみの一つです。

 さて、これでメインウィンドウが切り替わった時にImageInfoが更新され、通知も行われるようになりましたので、後はこの通知をパネルが受け取り、そのタイミングで更新された内容を表示するだけです。まずは、デフォルトのNSNotificationCenterに、「ImageInfoが変わったぞ」という通知を受け取りたいということを伝えましょう。InfoPanelController.mのwindowDidLoadに、その処理を書き加えて下さい。

- (void)windowDidLoad {
    [ self updateImageInfo];
    [ [ NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector( updateImageInfoHandle:) name:TNVWImageInfoChangedNotification
 object:nil];
}

 下から2行目が付け加えた行です。まず、デフォルトのNSNotificationCenterを取得するのは通知の時といっしょですね。これに対して、addObserver:selector:name:object:メッセージを投げています。このメッセージを投げることにより、通知の送信先を登録することができます。これをオブザーバと言います。ここでは自分自身に通知して欲しいので、第1引数でselfを指定しています。第3引数は、受け取りたい通知の名前を指定します。ここにnilを指定すると、何でも受け取りたいということになりますが、今回は「ImageInfoが変わったぞ」という通知だけを受け取りたいので、前回出て来たTNVWImageInfoChangedNotificationを指定しています。第4引数は、特定のオブジェクトからの通知だけ受け取りたい時に、そのオブジェクトを指定します。今回は、どこから受け取ってもかまわないので、nilを指定しています。では、第2引数は何でしょうか。NSNotificationCenterは、通知を受け取ると、登録されているオブザーバに対し、NSNotificationクラスのインスタンスを引数とするメッセージを投げます。第2引数は、この時投げて欲しいメッセージを指定します。つまり、上記の様にしてオブザーバを登録した時は、次のメソッドを実装しなければなりません。

- (void)updateImageInfoHandle:(NSNotification *)notif;

 これを実装しましょう。実装と言っても、既にパネルを更新する処理は作ってありますから、たったの一行です。

- (void)updateImageInfoHandle:(NSNotification *)notif{
    [ self updateImageInfo];
}

 これで通知を受け取る処理はできました。ビルドして実行すれば、メインウィンドウが切り替わるたびに、パネルの表示も切り替わるようになったはずです。でも、それはちょっと待ってください。今のままですと、極稀に終了時に吹っ飛ぶかもしれないプログラムになっています。実を言えば、NSNotificationCenterは、オブザーバを登録する時にretainしていません。従いまして、登録したオブザーバが消滅する前に登録を削除しないと、存在しないオブジェクトにメッセージを投げることになってしまいます。そこで、InfoPanelControllerのdeallocをオーバライドし、オブザーバの削除を付け加えて下さい。

- (void)dealloc {
    [ [ NSNotificationCenter defaultCenter] removeObserver:self 
name:TNVWImageInfoChangedNotification object:nil];
    [ super dealloc];
}

 これで問題は無くなりました。ビルドして実行してみてください。
 ところで、何でNSNotificationCenterは登録されたオブザーバのretainを行わないのでしょうか。第10回から第13回で説明したように、本来は自分にとってそのオブジェクトが必要であれば、必ずretainすべきです。そうすれば、知らない間に消滅していることがありません。ところが、第13回でも説明したように、cocoaではメッセージを投げて欲しい先を登録するような時は、retainを行いません。これが何故なのか、私も正確な所は知らないのですが、例えばNSNotificationCenterがretainしてしまうと、今回の様にオブジェクトが消滅する時にオブザーバを登録から削除するようにすることが出来なくなってしまいます。何故なら、NSNotificationCenterがretainしている限り、決してdeallocは呼ばれないからです。この様に、retainするとかえって不便になってしまうことから、メッセージを投げて欲しい先を教える場合は、物としてオブジェクトを渡す場合と区別しているのではないかと推測しています。
 それでは、続きは次回です。

小池邦人の「Carbon API 徒然草」(2004/04/16)

コールバックルーチンとメニューの初期化

今回は、初期化ルーチンのstartApplication()で実行しているsetupDrag()とsetupMenu()の内容を解説します。

本連載で紹介しているサンプルアプリケーションでは、画像ファイル(アイコン)を直接ウィンドウ上にドラッグ&ドロップすることで、その情報をブラウザに登録することが可能です。setupDrag()では、そのドラッグ&ドロップ処理で使用する2つのコールバックルーチン(Callback Routine)を登録しています。

void setupDrag(void)
{
    InstallTrackingHandler( (DragTrackingHandlerProcPtr)myTrackDrag,NULL,NULL );
    InstallReceiveHandler( (DragReceiveHandlerProcPtr)myReciveDrag,NULL,NULL );
}

コールバックルーチンとは、APIに制御が渡っている間、一時的に制御をアプリ側に返すためにAPI側から呼ばれるルーチンのことです。例えば、ウィンドウに表示された画像をスクロールしようとします。ユーザはスクロールバーの青い部分(スクローラーと言う)をマウスドラッグで動かすことで画像をスクロールします。もしその間にアプリ側で表示制御ができないと、画像スクロールはマウスボタンを離すまで実行されません。スクローラーをマウスで動かしている間もリアルタイムで画像をスクロールさせたい(ライブスクロールと言う)場合には、SetControlAction() というAPIでコントロールにコールバックルーチンを登録し、そこに表示用の制御を記述する必要があるわけです。

setupDrag()において、InstallTrackingHandler()は「ドラッグ中」(アイコンをドラッグしている間)に呼ばれるコールバックルーチン(myTrackDrag)を登録しています。もう片方のInstallReceiveHandler()は、「ドロップ時」(アイコンを離した瞬間)に呼ばれるコールバックルーチン(myReciveDrag)を登録しています。それぞれのコールバックルーチンに渡される引数や返り値は、その処理が関係しているヘッダーファイルにすべて定義されています。ドラッグ&ドロップのコールバックルーチンについては、Carbon FrameworkのDrag.cを参照してください。

pascal void myTrackDrag( DragTrackingMessage mes,WindowPtr window,
                                          long *refcon, DragReference dref);
pascal short myReciveDrag(WindowPtr window, long *refcon, DragReference dref);

後は、myTrackDrag()とmyReciveDrag()という2つのルーチンを用意し、その中身(処理)をルールに従い記述さえすれば、目的に応じたドラッグ&ドロップ処理が実装できます。myTrackDrag()とmyReciveDrag()の内容については、後ほど本連載の中で詳しく解説したいと思います。ちなみにMac OS 9環境のコールバックルーチンの登録は、以下のように記述されていました。

void setupDrag(void)
{
    DragTrackingHandlerUPP   dupp1;
    DragReceiveHandlerUPP    dupp2;

    dupp1=NewDragTrackingHandlerUPP( (DragTrackingHandlerProcPtr)myTrackDrag );
    dupp2=NewDragReceiveHandlerUPP( (DragReceiveHandlerProcPtr)myReciveDrag );

    InstallTrackingHandler( dupp1,NULL,NULL );
    InstallReceiveHandler( dupp2,NULL,NULL );
}


ここで登場しているDragTrackingHandlerUPPやDragReceiveHandlerUPPは、「Universal Procedure Pointer(UPP)」と呼ばれています。これは、MacintoshのCPUがPowerPCに移行した時、内部的にまだ残っていた68Kルーチンと新しいPPCルーチンの参照方法の違いを吸収するために導入された仕組みです。UPPはAppleのサンプルコードなどでもよく登場しますが、Mac OS X環境でMach-O(ネイティブ)Carbonアプリケーションを作成する場合には、いちいち用意する必要はありません。本当はコールバックルーチンの先頭に付いている(DragTrackingHandlerProcPtr)や(DragReceiveHandlerProcPtr)といったキャストも不必要なはずなのですが…どうもDrag.hの定義忘れ(バグ?)で、これだけは記述しないとコンパイルエラーが発生するようです。

続いて、Nibファイルのメニューオブジェクトを呼び出し初期化するsetupMenu()ルーチンです。メニューは「main.nib」ファイルに「MenuBar」というオブジェクト名で登録されています。CreateNibReference()で対象Nibファイルとしてmainを指定し(ファイル名の拡張子は不要)SetMenuBarFromNib()でMenuBarオブジェクトを呼び込みます。CFSTR()はAPIではなく、C文字列を渡しCFStringRef定数(コンスタント)を返すマクロです。これは、CoreFoundation FrameworkのCFString.hに定義されています。実際にCFStringRefオブジェクトをコピーしたり作成したりしているわけではありませんので注意してください。

void setupMenu(void)
{
    IBNibRef    nref;
    MenuRef     mhd;

    if( ! CreateNibReference( CFSTR("main"), &nref ) ) // main.nibファイルを指定
    {
        SetMenuBarFromNib( nref,CFSTR("MenuBar") );     // MenuBarを呼び込む
        DisposeNibReference( nref );
    }
    DisableMenuCommand( NULL,kHICommandPreferences );   // 初期設定はハイライト
    CreateStandardWindowMenu( 0,&mhd );                // ウィンドウメニュー作成
    InsertMenu( mhd,0 );                               // ウィンドウメニュー登録
    DrawMenuBar();                                     // メニューバー表示
}


Mac OS Xのアプリケーションメニュー(アップルメニュー左側)には、「環境設定…」「サービス」「…を隠す」「すべて表示」「終了」といったアイテムが自動で追加されます。本サンプルでは環境設定ダイアログを使わないので、DisableMenuCommand()によりアプリケーションメニューの「環境設定…」アイテムをハイライト(使用不可)にします。引数のkHICommandPreferencesは、CarbonEvents.hにおいて’pref’と定義されています。つまり、Manu Managerに対して「Carbon Eventのコマンドとして’pref’が定義されているメニューアイテムをハイライトにしろ!」と命令しているわけです。次のCreateStandardWindowMenu()は、標準のウィンドウメニュー構築するAPIです。得られたMenuRef(mhd)をInsertMenu()に渡し、ウィンドウメニューをメニューバーの一番最後に追加しています。

InsertMenu()に渡す引数ゼロは「このタイトルをすべてのメニュータイトルの後に挿入しろ」という意味です。代わりに適切なMenuIDを渡せば、そのタイトルの左側にウィンドウメニューが挿入されます。例えばゼロの代わりに128を代入し、メニュー表示がどうなるか試してみてください。こうして初期化されたメニューバーは、イベントループで最初のCarbon Eventが処理される時に自動描画されます。今回については、直ちに描画させるためにルーチンの最後にDrawMenuBar()を呼んでいます。このDrawMenuBar()を外すことでメニューバー描画のタイミングがどのように変化するかを試してみるのも面白いでしょう。Menu Managerに関する各APIの詳しい内容については、ヘッダーファイルのMenu.hで参照することができます。

次回は、アプリケーション起動時やファイルオープン時に受け取ったApple Eventをどのように処理するかについて解説いたします。Apple EventとCarbon Eventの違いについても簡単にお話したいと思います。

つづく

「Behind the WebObjects」  第18回  田畑 英和

 今回はDirectAction内でのリクエストパラメータのハンドリング方法について解説します。セッションを用いた処理の場合は、wodファイルのバインド情報にもとづいて自動的にリクエストパラメータが適切な変数へと代入されますが、DirectActionの場合は明示的にパラメータをハンドリングする必要があります。これは典型的なCGIプログラミングに似ていますが、WebObjectsではリクエストデータはオブジェクトとして扱われますので、HTTPのデータ形式についてまでは意識する必要はありません。だからといってデータ形式について理解していなくてもよいということにはなりませんので、まずはどのような形式でHTTPのリクエストデータが送信されてくるかをみてみましょう。

・HTTPデータのサンプル
key1=123&key2=abc

 例えばこのようなデータがHTTPのリクエストに含まれています。HTTPのリクエストはkeyとvalueのペアから構成され”key=value”のように’='の左側がkeyで右側がvalueになります。複数のkey-valueのペアが存在する場合は’&'で区切られるようになっています。
 こういった形式のリクエストデータはWebObjectsではWORequestのオブジェクトとして扱われます。DirectAction内でWORequestのオブジェクトを取得する方法ですが、次のAPIを用います。

・リクエストオブジェクトの取得
public WORequest request()

 このメソッドはWODirectActionの親クラスのWOActionで定義されています。リクエストオブジェクトの生成はWebObjectsのフレームワーク側で自動的におこなわれますが、DirectActionでリクエストオブジェクトから必要なデータを取り出すにはプログラミングをおこなう必要があります。リクエストオブジェクトが管理するデータにアクセスするには次のようなAPIが用意されています。

・WORequsetのメソッド
public Object formValueForKey(String aKey)
public String stringFormValueForKey(String aKey)
public NSArray formValueKeys()
public NSDictionary formValues()

 formValueForKeyではkey名を指定することにより、リクエストデータの中から任意のvalueを取り出すことができます。formValueForKeyの場合Object型のオブジェクトが返されますが、stringFormValueForKeyの場合はString型のオブジェクトが返されます。
 リクエストデータに含まれる全てのkeyを調べたいときにはformValueKeysを用いると、NSArrayとしてすべてのキーの一覧を取得することができます。またformValuesではすべてのkeyとvalueのペアをNSDictionaryのオブジェクトとして取得することができます。
 これらのAPIを用いて上記のHTTPデータのサンプルにアクセスするには、例えば次のようなコーディングをおこないます。

・サンプルプログラム

 WORequest request = request();
     Object obj = request.formValueForKey("key1");
     System.out.println("key1 = " + obj);
     String str = request.stringFormValueForKey("key2");
     System.out.println("key2 = " + str);


・実行結果
key1 = 123
key2 = abc

 なおこのようなリクエストデータへの直接のアクセスはDirectAction内にかからわらず、WOComponent内でもおこなうことができます。WOComponet内でリクエストオブジェクトを取得するには以下のようにコーディングします。

・WOComponet内でのリクエストオブジェクトの取得

     WORequest request = context().request();


 このようにしてDirectAction内で任意のリクエストデータをハンドリングすることができます。HTMLのFormによって入力したデータがリクエストデータとして送信されますが、WebObjectsアプリケーション内でDirectActionを呼び出した場合には自動的に追加されるパラメータがあります。
 前々回にも紹介しましたが、セッションをもったページからDirectActionに対してHTML FormをSubmitした場合、リクエストデータに自動的にセッションIDが追加されます。自動的に埋め込まれるセッションIDは、DirectActionを呼び出したページのセッションIDになります。
 具体的には次のようにURLおよびFormのhiddenタグとしてセッションIDが埋め込まれDirectActionへのリクエストデータとして送信されます。

・URL
…/wa/default?wosid=anfSeeYQHtNBebLfZD9Ae0
・hidden tag

 このようにリクエストデータにセッションIDが埋め込まれるため、リクエストを受け取ったDirectActionではどのセッションからリクエストが送信されたかを識別することが可能になります。
 それでは、次回はセッションとDirectActionを組み合わせた処理について解説します。

ニュース・解説

 今週の解説担当:新居雅行

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃FileMaker Pro 7は5月中旬に発売
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
すでにアナウンスされているFileMakerの次期バージョン「FileMaker 7」であるが、日本でも製品発表が行われた。FileMaker 7 Proが 39,000で2004年5月中旬、FileMaker Developer 7が 69,00で同じく5月中旬に発売される。
FileMaker 7 ProへのアップグレードはVer.2.1以降のユーザなら可能で価格は19,000となっている。なお、FileMaker Server 7は 129,000で2004年夏、FileMaker Mobile 7は 8,900で2004年夏、FileMaker Server 7 Advancedは289,000で2004年秋にリリースが予定されている。
FileMaker 7はMac OS X、Windows 2000/XPの対応となっているため、従来のOS利用者も考慮して、ファイルメーカーPro 6などの現行製品も2004年内は販売し続ける予定である。
FileMaker Pro 7の大きな変化は、リレーショナルデータベースとしての機能が充実したことがまず挙げられる。これまでもリレーションは可能だったが、ファイル単位にテーブルが1つだけだったので、制約された機能ではあった。
しかしながら、Ver.7では1つのファイルにテーブルを複数定義でき、そのテーブル間のリレーションもグラフィカルに定義できるなど、リレーショナルデータベースとしての機能が向上した。また、データベースのスケーラビリティも向上した。ファイルサイズは8TBとなるなど、上限はほとんど現状では制約にならないだろう。テキストが64KBまでしか1つのフィールドに入らなかったのが2GBになり、もはや制限は気にしなくてもいいくらいだろう。さらに、オブジェクトフィールドに直接ファイルをインポートしたり、あるいはフォーム内でPDFを参照できるなど、テキスト以外のデータの扱いも向上している。
開発者にとって便利になる機能もいろいろあるが、まずはフィールド定義の計算式の中で、変数が使えるようになったことだ。事実上、式というよりも、式を求めるプログラムを書けるレベルになったと言えるだろう。テキストの一部の書式をいじるといった関数や、指定した文字以外を取り除くフィルタ関数など、便利な関数が増えている。また、スクリプトにパラメータを指定することが可能になったので、ちょっとずつ設定の違うスクリプトを大量に作るという手間は必要なくなるだろう。1つのデータベースでウインドウを複数開くことができるようになったことも便利になった点だ。さらに、Developer版を使えばカスタム関数の定義ができ、そこで作った関数は通常のFileMaker Pro 7でも実行できる。
ファイルメーカー社では無償のソリューション配布を行っているが、今後も積極的にソリューション展開を行い、ソリューションにフォーカスしたセミナーなども計画している。また、システムインテグレータ支援にも力を入れるなど、より積極的な展開を進めるとしている。

ファイルメーカー
http://www.filemaker.co.jp/

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Rendezvous文書からMac OS Xの機能を探る
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Rendezvous関連のTechnical Q&Aが2つ新たに公開されているので、紹介しておこう。

Technical Q&A: Rendezvous service types used in Mac?OS?X
http://developer.apple.com/qa/qa2001/qa1312.html

Rendezvousが使っているサービス名の一覧がまとめられている。サービス名称はある意味、裏の名前でもあるが、この表を見ると、FTPやssh、telnetといったサービスまでRendezvous対応していることが分かる。逆に言えば、Mac OS Xがクライアントとして、どのプロトコルのブラウズをRendezvousで行っているかを見ることができると言えるだろう。たとえば、Mac OS XやMac OS X Serverのsshのサーバ(sshd)はRendezvous対応しているため、ターミナルで接続先のウインドウを表示すると、サーバを自動認識するといったことができる。

Technical Q&A: NSL and how it relates to Rendezvous
http://developer.apple.com/qa/qa2001/qa1299.html

従来からあるNSL(Network Service Location)とRendezvousの解説である。NSLは古くはSLP(Service Location Protocol)やAppleTalkを使っていたが、基本的にはプロトコルと独立したネットワーク上のサービスを参照するための手法となっている。しかしながら、Mac OS X 10.2よりRendezvousを採用しているため、NSLではなくRendezvousを使ったネットワークサービスのブラウズを行うようにすべきだと説明されている。NSL APIは、ネットワーク対象のブラウズダイアログ以外はRendezvous対応はしていないということである。

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Webアプリケーションを生成するビジュアル開発ツール
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BasesoftがリリースするBaseOneは、Mac OS Xで利用できる開発ツールで、Webベースのアプリケーションを生成することが可能だ。ウインドウにボタンやテキストフィールドなどのコンポーネントを配置し、アプリケーション内でテスト動作が可能である。スクリプトとしてJavaScriptなどを追加できると同時に、データベース接続定義を行うことで、データベース処理を組み込んだアプリケーションが生成される。データベースはMySQL、サーバサイドのテクノロジーはPHPに対応している。生成されるJavaScriptでは、ブラウザ毎に異なるスタイルシートを利用するなど、CSSを利用してレイアウトの再現性を高めている。価格は119ユーロだが、14日間のデモライセンスを利用して試用することができる。

BaseOne
http://www.basasoft.com/basaone.php

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃Mac OS X ServerのユーザパスワードをWebで変更
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Mac OS X ServerでのユーザのパスワードをWebブラウザから変更できるOSXPASSが正式リリースとなっている。以前から開発はされていたが、Ver.2.0としてアップデートされ、さらにC言語で記述したVer.2.1がリリースされている。オープンソースで開発されているので、ページのデザインなどのカスタマイズもやりやすいだろう。開発はeyebits studios。
Mac OS X Server 10.3ではLDAPをベースにしたディレクトリサービスのもと、パスワードサーバが稼働してパスワード管理をしている。パスワードの変更はMac OS Xでのログインなどで不可能ではないが、たとえばWindowsクライアントや、メールサーバとしてだけの利用を行う場合などではパスワード変更の方法が提供できないか極めて限定していた。ビルドされたCGIと画像のファイルをサーバに配備し、WebサービスでCGI実行権限を付ければ、すぐに利用できる。パスワードが修正されたかどうかは、オープンディレクトリのパスワードサービスのログで確認もできる。

OSXPASS
http://sourceforge.net/projects/osxpass/
パスワードを変更するページ
http://msyk.net/mosa/DeN/018.png

MOSAからのお知らせと編集後記は割愛します

MOSA Developer News   略称[MOSADeN=モサ伝]
Apple、Mac OSは米国アップルコンピュータ社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
http://www.mosa.gr.jp/
Copyright (C)2004-2006 MOSA. All rights reserved.