2005-04-12
目次
「WebObjects Dev Report」 第2回 田畑 英和
Tigerの情報も徐々に公開されてきたようで、そのリリースが待ち遠しいですが、6月にはWWDCも控えておりなにが発表されるやら楽しみです。Tiger環境でWebObjectsがどのようにサポートされるかなども情報があきらかになった時点で、この連載を通して取り上げて行きたいと思います。
サーバ環境はなかなか気軽にバージョンアップできない場合が多く、今でもまだJaguar(10.2)で運用しているシステムもあったりするかもしれませんが、実際にサーバ機のOSをアップデートするとなると一時的にではあるにせよサービスを停止する必要もでてきますし、そういった運用面のこともいずれ取り上げていければと思います。
さて、プロジェクトを開始するにあたってなにか名前がないことにはどうにも収まりが悪いわけですが、まずはプロジェクト名から決めていきたいと思います。こういった名前を思いつきで適当に決めてしまいますと後になって後悔したり、決めたはいいがプロジェクトの方向性がころころ変わってしまい、最初に決めた名前とはかけ離れたものになってしまった、なんてこともありますが、まずは名前を決めるところから始めてみましょう。
構想時間3秒で思いついた名前が「WOMatch」です。このプロジェクトではWebObjectsで「ジョブマッチングシステム」を開発することからWO + Matchingということで「WOMatch」になったというわけです。安易な名前付けかもしれませんが、なにより分かりやすい名前ですしXcodeのプロジェクトもこの名前で作成していきたいと思います。
あと名前を付けるときに注意しなければならないこととして、すでに使われている名前と重複していないかのチェックがあります。特に商品やサービスの名前としても使うような場合は、商標としてすでに登録されていないかなどの確認が必要になってきます。ちなみにGoogleで「WOMatch」を検索してみたところ3件でした。はたして、このプロジェクトはGoogleでひっかかる日がくるでしょうか。
話題は少しそれてしまいますが、Googleといえば地図サービスのβ版を提供しており、衛星画像の表示機能も加わったようです。住所を打ち込むだけでなんとApple本社のビルまで、バッチリと見れてしまいます。
・Google Maps(BETA)
http://maps.google.com/
・Apple本社住所
1 Infinite Loop, Cupertino, CA 95014
このGoogle Mapsのサイトにアクセスするとそのレスポンスの良さにすぐ気付くかと思いますが、最近このようなJavaScript等の技術を駆使したサイトが注目を集めており、Ajax(Asynchronous JavaScript + XML)といった言葉も生まれてきています。
「WOMatch」でこういった技術を取り込んでいけるかどうかはまだ分かりませんが、Flashに頼らずとも既存のWeb関連技術を組み合わせればかなりのことができるようになってきています。Tigerで導入されるDashboardも将来は他のプラットフォームに派生していくことも十分考えられますし、Webの世界もまだまだ進化の余地があるということですね。
さて、本題に戻りまして、先日本プロジェクトの今後の方針を決める為にMOSA事務局にて関係者の打ち合せがおこなわれましたのでそちらの報告をしておきたいと思います。
現在のビジネスマッチングでは主に技術者(あるいは企業)が各自のスキル情報を公開してコンタクトを待つといった使われ方がされていますが、これですとマッチングとしては片方向だけになってしまいますので、まずは逆のパターン、つまり案件の情報を提示して技術者を募集するといった機能を盛り込んでいこうということになりました。
またその他の機能的なこととしましては、スキル情報だけが掲載されていても実際のところどうなのかよく分からないという問題がありますので、第三者の評価機能も必要になってきます。Yahooオークションの出品者評価あるいはMixiの紹介文といったところでしょうか。ここまできますとソーシャルネットワーク的な要素も加わってくることになり、アクセス権も会員限定の部分と一般に公開する部分とに分かれてくるでしょう。
さらにはE-mailでの新着情報自動配信ですとか、会員DBの一元化といった課題も上がっています。現在はシステムごとにデータ管理が分かれていますのでそちらを統合することにより、より効率的な運営が可能になるというわけです。
というわけで今回も概要的な話だけになってしまいましたが、次回からは開発をスタートするにあたってどのような準備をしていけばよいのかといったことを解説していきたいと思います。
小池邦人の「Carbon API 徒然草」(2005/04/08)
Finderからアプリケーションへのアイコン(ファイルやフォルダ)のドラッグ&ドロップは、もうひとつのファイルのオープン方法として、Macユーザの間ではすっかりお馴染みです。今回から数回に分けて、こうしたドラッグ&ドロップ処理のアプリケーションへの実装について解説したいと思います。
ウィンドウ上へドラッグ&ドロップされたアイコンから情報を得て、そのファイルをオープンするといった処理は、数多くのアプリケーションで採用されています。こうした処理は、Drag ManagerのAPIを使うことで実現します。本サンプルアプリケーションでも、画像ファイルやフォルダ(Finderのアイコン)を直接カタログウィンドウのブラウザ上にドロップすることで、その情報を登録することが可能です。以前、「コールバックルーチンの初期化」を紹介した時にもお話しましたが、ドラッグ&ドロップを利用するには、まずは2つのコールバックルーチン(Callback Routine)をシステムに登録(インストール)する必要があります。それを実行しているのがsetupDrag()ルーチンです。
void setupDrag(void)
{
InstallTrackingHandler( (DragTrackingHandlerUPP)myTrackDrag,NULL,NULL );
InstallReceiveHandler( (DragReceiveHandlerUPP)myReciveDrag,NULL,NULL );
}
コールバックルーチンとは、システムに制御が渡っている間、一時的に制御をアプリ側に戻すためにシステム側から実行されるルーチンのことです。setupDrag()では2つのコールバックルーチンを登録しています。そのうち、InstallTrackingHandler()で登録されているmyTrackDrag()ルーチンは、アイコンをドラッグしている間に継続して呼ばれます。また、InstallReceiveHandler()で登録されているmyReciveDrag()ルーチンは、アイコンをドロップした瞬間に呼ばれます。
各コールバックルーチンに渡される引数や返り値は、Universal InterfacesのDrag.hに定義されていますので、そちらを参照してください。Mac OS X 10.1から「DragActions」に関係するAPIがいくつか追加されましたが、それ以外の仕組みはMac OS 9の時代からからほとんど変更ありません。以前にも言及しましたが、setupDrag()でコールバックルーチン名の先頭に付けている(DragTrackingHandlerProcPtr)や(DragReceiveHandlerProcPtr)といったキャストは、Mac OS Xネイティブアプリケーションであれば不必要なはずです。しかし、Drag.hの定義内容にミスがあるのか、キャストを記述しないとコンパイルエラーが発生しますので注意してください。
通常、Drag Managerを利用する場合には、次の3つの処理を実装する必要があります。まずは、ドラッグ最中の処理(Tracking処理)、そしてドロップされたデータを受け取る処理(Receive処理)、最後に自分のデータをドラッグして相手に渡す処理(Send処理)です。本サンプルアプリケーションでは、アプリケーション側のデータを別アプリ(Finderなど)に渡す機能はありませんので、Tracking処理とReceive処理のみを実装します。まずは、Tracking処理の方を見てみることにします。Tracking処理の目的は、ユーザにドロップ可能なターゲット領域(通常は矩形枠)を知らせるために、その領域を強調色(ハイライト色)で表示し直すことにあります。そのためには、ドラッグされているデータ(PICTとかファイル保存場所)が受け取り可なのか不可なのかを判断する必要もあります。以下が、InstallTrackingHandler()でシステムへ登録されたmyTrackDrag()ルーチンです。
#define BROW_ID 100 // ブラウザコントロールのID番号
long d_acc; // ドラッグデータのドロップを許しても良いかどうか?
long d_cnb; // 強調色で表示されているターゲット領域の矩形番号
pascal void myTrackDrag( DragTrackingMessage mes,WindowPtr window,
long *refcon,DragReference dref )
{
Rect drt[32]; // ターゲット領域は32個まで設定可能(今回はひとつ)
short ct;
Point pt;
switch( mes )
{
case kDragTrackingEnterWindow: // ドラッグがウィンドウ領域へ入る
d_acc=trackDragAcceptType( window,dref ); // 受け取れるデータか?
d_cnb=0; // ターゲット領域の矩形番号を初期化する
break;
case kDragTrackingInWindow: // ドラッグがウィンドウ領域内を移動中
if( ! d_acc )
break;
GetDragMouse( dref,&pt,0L ); // ドラッグのマウス位置を得る
GlobalToLocal( &pt ); // マウス位置をローカル座標に変換
if( ! trackDragInWindow( window,dref,pt,drt,&ct ) )
// ウィンドウ内にターゲット矩形領域があるか?
trackDragDrawHilite( dref,pt,drt,ct ); // あれば矩形枠を強調
break;
case kDragTrackingLeaveWindow: // ドラッグがウィンドウ領域から外れる
if( d_acc && d_cnb )
HideDragHilite( dref ); // ターゲット矩形領域の強調色を元に戻す
d_acc=0;
break;
}
}
myTrackDrag()では、ドラッグデータの状態が引数で渡されるDragTrackingMessageの値によって判断できる仕組みになっています。メッセージには、kDragTrackingEnterWindow、kDragTrackingInWindow、kDragTrackingLeaveWindowの3種類があり、それぞれドラッグされているデータ(例えばアイコン)がウィンドウ領域へ入った時、ウィンドウ領域内を移動している時、ウィンドウ領域から外れた時に伝達されます。まずは、ドラッグがウィンドウ領域へ入った時に、そのデータが受け取り可能かどうかをtrackDragAcceptType()ルーチンで判断します。
short trackDragAcceptType( WindowPtr window,DragReference dref )
{
FlavorFlags flag;
ItemReference item;
unsigned short i,ct;
CountDragItems( dref,&ct ); // ドラッグされているデータの個数を得る
for( i=1;i<=ct;i++ )
{
GetDragItemReferenceNumber( dref,i,&item ); // リファレンス番号を得る
switch( GetWRefCon( window ) ) // ウィンドウの種類を判断する
{
case 'CATA': // ターゲット領域がカタログウィンドウ内の場合
if( GetFlavorFlags( dref,item,'hfs ',&flag )==0 )
// データタイプが'hfs 'かどうかチェックする
return( 1 ); // これは受け取れるデータである
break;
}
}
return( 0 ); // 受け取れるデータはひとつもない
}
今回はターゲット領域となるウィンドウはカタログウィンドウのみで、それ以外のウィンドウ(画像表示ウィンドウなど)はアイコンドロップのターゲット領域とはなりません。その判断は、GetWRefCon()で得たウィンドウタイプで判断します。ターゲットがカタログウィンドウであれば、GetFlavorFlags()を使いドラッグされているデータの種類が受け取り可能かどうかを判断します。今回はドラッグされているデータタイプが'hfs 'の場合にのみ受け取ることができます。もし、受け取れると判断されれば、trackDragInWindow()により、ターゲット領域を特定します。
short trackDragInWindow( WindowPtr window,DragReference dref,Point pt,
Rect drt[],short *ct )
{
short ret=0;
ControlRef chd;
*ct=0;
if( d_acc==1 )
{
switch( GetWRefCon( window ) ) // ウィンドウの種類を判断する
{
case 'CATA': // ターゲット領域がカタログウィンドウ内の場合
*ct=1; // ターゲット領域はひとつだけ
getMyControlRef( window,BROW_ID,&chd );
// ブラウザコントロールのControlRefを得る
GetControlBounds( chd,&drt[0] ); // ブラウザの矩形枠を得る
break;
}
}
return( ret );
}
ドラッグ&ドロップ処理ではウィンドウ全領域がターゲット領域となる場合が多いのですが(Finderなどがそう)、今回はウィンドウ内のブラウザコントロールの矩形枠が唯一のターゲット領域となります。もし、ウィンドウ内に複数のターゲット領域が存在しているとすれば、外部変数のd_cnbにその識別番号(1から始まる)を記録して強調枠を描いたり削除したりする場合の判断に用います。以下が、ターゲット領域の矩形枠を強調色で描画するtrackDragDrawHilite()ルーチンです。
void trackDragDrawHilite( DragReference dref, Point pt,Rect drt[],short ct )
{
short i,now=0;
RgnHandle rgn;
for( i=0;i<ct;i++ ) // ターゲット領域の個数分ループさせる
{
if( PtInRect( pt,&drt[i] ) ) // ドラッグ位置を確認
{
now=i+1; // 現在のターゲット領域番号をセット
break;
}
}
if( now && d_cnb!=now ) // 新しいターゲット領域に入った場合
{
if( d_cnb ) // もし前に別のターゲット領域へ入っていれば...
{
HideDragHilite( dref ); // 前のターゲット領域の強調色を削除
d_cnb=0; // ターゲット領域番号をクリア
}
if( rgn=NewRgn() ) // 描画用のRegionを作成
{
RectRgn( rgn,&drt[now-1] ); // Regionに矩形枠をセット
ShowDragHilite( dref,rgn,1 ); // ターゲット領域を強調色で描画
DisposeRgn( rgn ); // Regionを削除
d_cnb=now; // 現在のターゲット領域番号をセット
}
}
else if( now==0 ) // ターゲット領域から外れた場合
{
if( d_cnb ) // もし前に別のターゲット領域へ入っていれば...
{
HideDragHilite( dref ); // 前のターゲット領域の強調色を削除
d_cnb=0; // ターゲット領域番号をクリア
}
}
}
もし、ターゲット領域をウィンドウ枠ひとつだけに限定すれば、このルーチンはずっと簡単な処理となります。現在のDrag Managerではウィンドウのテーマ(背景)が「しましま」であると、何故だかShowDragHilite()を実行しても強調色で矩形が描画されません(涙)。背景が真っ白なドキュメントウィンドウならOKなのですが、「しましま」テーマであっても何らかの強調を行うのが、ユーザインターフェースガイドライン的には正しいと思うのですが....。
次回は、Finderからドラッグされてきたアイコンがターゲット領域(ブラウザ)へドロップされた瞬間の処理、つまりInstallReceiveHandler()で登録されているmyReciveDrag()について解説したいと思います。
つづく
SqueakではじめるSmalltalk入門 第37回 鷲見正人
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、再びプロトコルに話を戻し、コレクションの探索を続けます。
まずは、ダブルディスパッチにからめて、飛ばしたadaptingプロトコルについてちょっとだけ。
adaptingプロトコルには、前回登場した#adaptToCollection:andSend:の他に、#adaptToNumber:andSend:、#adaptToPoint:andSend:、#adaptToString:andSend:が定義されています。それぞれに対応するクラス(Number、Point、String)かそのサブクラスに属するインスタンスがレシーバとなる二項演算メッセージにおいて、その引数がコレクションだった場合の対処方法が記述されています。それぞれのメソッドの定義を見て、次に示した式の結果について予想がつくかどうかで、ダブルディスパッチを理解できているかを確認してみるのもよいでしょう。
4 * #(1 2 3) " => #(4 8 12) "
(4 @ 5) * #(1 2 3) " => #(4@5 8@10 12@15) "
'456' * #(1 2 3) " => #(456 912 1368) "
adding、arithmeticは済んでいますね。comparingプロトコルは飛ばします。
次のconvertingプロトコルには、コレクション間の相互変換のためのメソッドがまとめてあります。原則として単項メッセージセレクタで、asの後に変換したいコレクションクラス名が続きます。
#(2 2 3 4 1 4 2 1 4) asBag " => a Bag(1 1 2 2 2 3 4 4 4) "
#(2 2 3 4 1 4 2 1 4) asSet " => a Set(1 2 3 4) "
#(2 2 3 4 1 4 2 1 4) asOrderedCollection
" => an OrderedCollection(2 2 3 4 1 4 2 1 4) "
#(2 2 3 4 1 4 2 1 4) asSortedCollection
" => a SortedCollection(1 1 2 2 2 3 4 4 4) "
'smalltalk' asArray " => #($s $m $a $l $l $t $a $l $k) "
'smalltalk' asBag " => a Bag($k $l $l $l $m $a $a $s $t) "
'smalltalk' asSet " => a Set($k $l $m $a $s $t) "
'smalltalk' asOrderedCollection
" => an OrderedCollection($s $m $a $l $l $t $a $l $k) "
'smalltalk' asSortedCollection
" => a SortedCollection($a $a $k $l $l $l $m $s $t) "
変換と言っても、レシーバのクラスが変更されるわけではなく、レシーバをもとに、指定した種類のコレクションが新しく作られることに注意してください。
ところで、新しくコレクションを作るには、この変換作業のほかに、作りたいコレクションが属するクラスに直接メッセージを送る方法もあります。オーソドックスには、まずnewを送信して空(から)のコレクションを作り、それに「add: anElement」などのメッセージを送って要素を追加してゆきます。
| collection |
collection _ OrderedCollection new.
collection add: 3; add: 2; add: 2; add: 1.
^ collection " => an OrderedCollection(3 2 2 1) "
| collection |
collection _ Set new.
collection add: 3; add: 2; add: 2; add: 1.
^ collection " => a Set(1 2 3) " "重複排除"
| collection |
collection _ SortedCollection new.
collection add: 3; add: 2; add: 2; add: 1.
^ collection " => a SortedCollection(1 2 2 3) " "ソート済み"
なおこの方法は、要素を増減させることができないコレクション(配列や文字列など)には使用できません。
Array new add: 3; add: 2; add: 2; add: 1 " => error "
この種のコレクションの場合には、たんにnewするのではなく、「new: size」で必要なサイズのコレクションを用意してから、あらためて各要素を置き換える手続きが必要です。
| array |
array _ Array new: 4.
array at: 1 put: 3.
array at: 2 put: 2.
array at: 3 put: 2.
array at: 4 put: 1.
^ array " => #(3 2 2 1) "
要素が六つより少ないなら空のコレクションを作らずに、いきなり「with: anElement」、「with: element1 with: element2」、「with: element1 with: element2 with: element3」…というメッセージを送信して済ませることもできます。
OrderedCollection with: 3 with: 2 with: 2 with: 1
" => an OrderedCollection(3 2 2 1) "
Set with: 3 with: 2 with: 2 with: 1 " => a Set(1 2 3) "
newしてから要素を追加するのと違って、この方法は配列や文字列にも使えます。
Array with: 3 with: 2 with: 2 with: 1 " => #(3 2 2 1) "
String with: $c with: $b with: $b with: $a " => 'cbba' "
要素が六つより多いときは、いったん要素を配列などに収めてから#withAll:で渡します。
OrderedCollection withAll: #(3 3 3 2 2 1 1 1 1)
" => an OrderedCollection(3 3 3 2 2 1 1 1 1) "
Set withAll: #(3 3 3 2 2 1 1 1 1) " => a Set(1 2 3) "
以上のようにクラスが受信を許すメッセージと、それによって起動されるメソッドについては、ブラウザの上段の左から二番目のペインの下にある、classボタンをクリックして閲覧モードを切り換えた後、instance creationプロトコルを選択することで、一覧したり、定義を見ることができます。
[fig.A]ブラウザでクラスプロトコルを閲覧しているところ。
http://squab.no-ip.com:8080/mosaren/uploads/37a.png
バックナンバー:
http://squab.no-ip.com:8080/mosaren/
ニュース・解説
今週の解説担当:木下 誠
----------------------------------------------------------------------
Core Dataを解説するドキュメントが公開
----------------------------------------------------------------------
Appleが、Tiger Developer Overview Seriesの最新作、「Developing with Core Data」を公開していました。ついに、Core Dataの全貌を表すドキュメントの公開が始まりました。Core Dataは、Cocoa Bindings以来の、Cocoaアプリケーションにやってきた大きな変革の波です。同時に、EOF (Enterprise Objects Framework)で培われた技術の、復活の狼煙でもあります。
Core Dataは、いままでCocoaフレームワークに欠けていた、モデルやストレージ周りのサポートを提供するものです。具体的には、アプリケーションで使うデータモデルのデザインとその管理、データのストレージへの保存と読み書き、アンドゥとリドゥのサポート、などが含まれます。
つまり、アプリケーションのMVC (Model-View-Controller)アーキテクチャの、モデル部のサポートとなります。これにより、ビューのサポートを行うNSViewクラス階層と、コントローラのサポートを行うCocoa Bindingsと合わせて、CocoaのMVCモデルは一つの完成を見た、といえるでしょう。
また、Core DataはWebObjectsのEOFに強く影響を受けており、EOFの復活と見ることも出来ます。このドキュメントでも、WebObjectsとの関係に言及しています。ただし、WebObjectsの、汎用的なデータベースアクセスフレームワークとしての側面は、Core Dataには取り入れられていません。データベースを必要としない、一般的なCocoaアプリケーションでも使える技術、という方向性を強調したいようです。
TigerのXcodeでは、Core Dataのためのデータモデリングツールが搭載されます。これは、アプリケーションで使うデータモデルをグラフィカルに編集するツールで、UMLでのクラス図のような図でオブジェクトグラフを表示します。このモデリングでは、エンティティ、属性、関係、プロパティといった、データベースやモデリングの分野で馴染みの深い言葉が登場します。
Core Dataで利用するクラスをいくつか紹介しましょう。エンティティをアプリケーションで実現するクラスとして、NSManagedObjectが実装されます。エンティティが持つプロパティは、NSManagedObjectによって管理され、KVC(Key-Value Coding)や、KVO (Key-Value Observing)で、アクセス、管理されます。つまり、Cocoa Bindingsと相性がいいということになります。そして、NSManagedObjectの集合は、NSManagedObjectContextによって管理されます。このクラスは同時に、エンティティの変化を監視することで、アンドゥ、リドゥのサポートも行います。NSManagedObjectContextとストレージの間を取り持つのが、NSPersistentStoreCoordinatorです。このクラスは、ストレージに保存されたデータと、メモリ上に展開されるオブジェクトの橋渡し役になります。
また、データモデルはInterface Builderとも統合されています。Appleお得意のゼロコーディングとして、Xcode上のモデリングツールから、Interface Builder上のウィンドウに、データモデルをドロップすることで、そのモデルを表示、編集するためのGUIが作成されます。Core Dataの機能を紹介するデモとして、非常に効果が高いです。
最後に余談ですが、Core Dataを表すアイコンが作られたようです。ストレージの中から、青い透明の球体がのぞいている絵です。Tigerのテクノロジのキーワードの一つは「Core」で、Coreを表すアイコンとして、内部からのぞく透明の球体が使われているようですね。
Developing with Core Data
http://developer.apple.com/macosx/tiger/coredata.html
----------------------------------------------------------------------
OCUnitのチュートリアル
----------------------------------------------------------------------
Appleが、「Test Driving Your Code with OCUnit」というドキュメントを公開していました。Objective-Cのユニットテストを行うフレームワークである、OCUnitの使い方を解説した、チュートリアルです。
モサ伝を読んでいる方なら、ユニットテストのことはご存知でしょう。むしろ、日々の業務で使っている方もいると思います。言語毎にいろいろなユニットテストのためのライブラリ、フレームワークがありますが、Objective-CのためのフレームワークがOCUnitです。
OCUnitの特徴は、Xcodeと統合されていることです。どういうことかというと、テストの結果をXcodeの警告、エラーウィンドウで表示することが出来ます。ということで、Objective-Cでのユニットテストを行う必要がある方は、目を通しておくと便利なドキュメントです。
ちなみに、Xcodeでの警告、エラーの表示は、テキストを読み込んで表示しているので、スクリプトで整形してやれば簡単に表示できるようです。
Test Driving Your Code with OCUnit
http://developer.apple.com/tools/unittest.html
Sen:te - OCUnit
http://www.sente.ch/software/ocunit/
----------------------------------------------------------------------
WWDC 2005関連情報
----------------------------------------------------------------------
「Meet the Engineers Behind Tiger」とうページが公開されていました。WWDCで説明をしてくれる、Appleのエンジニアやマネージャが紹介されています。写真がずらりと、あまりフォトジェニックとは言えませんが、並んでいます。
また、Apple Design Awardsの受付も始まっているようです。
WWDC 2005 - Meet the Engineers Behind Tiger
http://developer.apple.com/wwdc/engineers.html
WWDC 2005 - Apple Design Awards 2005
http://developer.apple.com/wwdc/ada/
MOSAからのお知らせと編集後記は割愛します