2005-02-01
目次
SqueakではじめるSmalltalk入門 第28回 鷲見 正人
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。
すでに耳タコ状態かもしれませんが、Smalltalkではすべてをメッセージ送信により表現します。前回解説したブロック、つまり「手続きをオブジェクトとして扱えるようにしたもの」は、制御構造などの通常の言語では構文として用意されている挙動もメッセージ送信で実現する際に役立てられています。実際の例で確認してみましょう。
▼条件分岐
これはもう何度も出てきていますので説明は不用ですね。レシーバがtrueあるいはfalse、それぞれの場合に評価したい手続きをパラメータ(引数)として添えたメッセージ送信式になっています。#ifTrue:ifFalse:の二つのパラメータ、つまり手続きは、いずれもブロック変数を持たないブロックでなければいけません。
名前を尋ねた後、入力文字列の有無を判断して声を掛けてくるスクリプトを示します。
| nameString |
nameString _ FillInTheBlank request: 'your name:'.
nameString isEmpty
ifTrue: [PopUpMenu inform: 'Nobody knows your name...']
ifFalse: [PopUpMenu inform: 'Hi, ', nameString, '!']
FillInTheBlankは、request: aStringというメッセージを受けると、aStringをラベルにした入力欄を表示します。Acceptボタンをクリックしたとき、入力済みの文字列を返値とします。Cancelボタンを押すと”、つまり、長さゼロの文字列が返ります。
isEmptyは、文字列や配列など、要素を持つオブジェクトに対して送信することで、それらに要素があればfalseを、要素なしならばtrueを返させるメッセージです。
” isEmpty ” => true ”
‘This is a pen.’ isEmpty ” => false ”
#() isEmpty ” => true ”
#(‘this’ is $a 10) isEmpty ” => false ”
PopUpMenuは、inform: aStringというメッセージを受けとることで、aStringというキャプションの付いた、「OK」というメニュー項目をひとつだけ持つポップアップメニューを作って表示します。
ところで、この制御構造も「式」なので、通常のメッセージ式同様に評価後には「値」を返します。#ifTrue:ifFalse:の場合、レシーバに対応したブロックの評価の結果がその返値です。つまり、レシーバがtrueなら第一パラメータにvalueを送信した結果が、falseなら第二パラメータにvalueを送信した結果が返ります。
このことを利用して、たとえば、上のスクリプトの場合、#ifTrue:ifFalse:のパラメータで共通するPopUpMenuへのメッセージ送信部分を括りだすことによる、記述の簡素化も可能です。
| nameString |
nameString _ FillInTheBlank request: 'your name:'.
PopUpMenu inform:
(nameString isEmpty
ifTrue: ['Nobody knows your name...']
ifFalse: ['Hi, ', nameString, '!'])
ここでPopUpMenuは、#ifTrue:ifFalse:の返値をパラメータにした#inform:を起動するメッセージを受信しています。意味や処理の順番は変わりますが、挙動は前のスクリプトとまったく同じです。
レシーバがtrueあるいはfalseの場合のみ処理を指定する#ifTrue:、#ifFalse:、パラメータの順序が逆の#ifFalse:iFTrue:も用意されています。
▼case-switch文のシミュレート
SqueakシステムのSmalltalkでは、#caseOf:あるいは#caseOf:otherwise:と独自のカーリーブレイス表記を組み合わせて用いることで、Smalltalk処理系としては珍しいcase-switch表現が実現されています。
(‘abc’ atRandom)
caseOf: {[$a] -> [1]. [$b] -> [2]. [$c] -> [3]}
atRandomは、文字列や配列など要素を持つコレクションオブジェクトから、任意の要素を取り出すメッセージです。この式では、atRandom送信によって’abc’から取り出された文字オブジェクト(a Character)の場合によって、1、2、3いずれかあらかじめ決められた数値を返します。
カーリーブレイス「{ }」は、リテラルで表現できない要素を持つ配列を直接作りたいときに用いる表現です。この例では[$a] -> [1]、[$b] -> [2]、[$c] -> [3]という三つの式の評価結果を要素とする配列の生成に使われています。
一見、特殊な表記のように見える#->もメソッドです。#@が「3 @ 4」などとすることでポイント(a Point)を作成したり、#/が「3 / 4」などで分数(a Fraction)を作成するのと同様に、レシーバを「キー(key)」に、パラメータを「値(value)」に据えた「関連(an Association)」というオブジェクトを作成します。case-switchでは、たまたま双方ともブロックが使われていますが、関連オブジェクト一般にはキー(レシーバ)と値(パラメータ)はどんなオブジェクトでも構いません。
関連オブジェクトのキーと値は、それぞれkey、valueというメッセージを送信することで参照できます。
(#one -> 1) key ” => #one ”
(#two -> 2) value ” => 2 ”
関連オブジェクトを単独で用いるようなことはあまりしませんが、辞書オブジェクト(a Dictionary)におけるキーと値の関係のように、二つのオブジェクトを関連付けるときに内部的によく利用されるので、名前や扱いは覚えておくとよいでしょう。
#caseOf:あるいは#caseOf:otherwise:は、キーと値それぞれにブロックを束縛した関連オブジェクトを要素に持つ配列をある種の辞書代わりに用い、キーを評価した結果がレシーバと一致した場合に、対応する値ブロックの評価を行なう…という仕組みでcase-switch文をシミュレートしています。
#caseOf:otherwise:では、第二パラメータにはいずれのケースにも当てはまらない場合の処理をブロックとして与えます。
このcase-switch表現を用いたスクリプトを次に示します。血液型を尋ねるポップアップが現われるので、項目を選択して答えると、おおざっぱな性格判断の結果を返してきます。
| menu bloodGroup |
menu _ SelectionMenu fromArray:
#(('A' #a)
('B' #b)
('AB' #ab)
('O' #o)).
bloodGroup _ menu startUpWithCaption: 'blood type:'.
PopUpMenu confirm:
(bloodGroup caseOf:
{[#a] -> ['nervous!'].
[#b] -> ['selfish!'].
[#ab] -> ['volatile!'].
[#o] -> ['optimistic!']} otherwise: ['fishy!'])
バックナンバー:
http://squab.no-ip.com:8080/mosaren/
小池邦人の「Carbon API 徒然草」(2005/01/28)
今回は、FSSpec、FSRef、AliasHandle、CFURLRefを用いたファイルアクセスの仕組みについてもう少し深く解説し、その例題として、FSSpecの代わりにFSRefを得ることが可能な「ファイル名入力ダイアログ」(シートウィンドウ版)を紹介します。
Mac OS Xでは、FSSpecの代わりにFSRefを用いてファイルアクセスすることで、Unicodeのロングファイル名(255文字)のサポート、大容量ファイル(4Gバイト以上)へのアクセス、ファイル処理のパフォーマンス向上などのメリットが得られます。Mac OS X 10.3からのCarbon FrameworkやQuickTime 6では、ファイルアクセスをFSSpecのみに依存しているAPIは拡張され、FSRefの併用もOKとなりました。ですから、これからのCarbonアプリケーションでは、積極的にFSRefを利用することが可能です。
まず最初に、FSSpecとFSRefの両構造体を示して比較してみます。
struct FSSpec {
short RefNum; // ボリュームリファレンス番号
long parID; // ディレクトリ ID番号
StrFileName name; // 63バイトパスカル文字列(Str63)
};
struct FSRef {
UInt8 hidden[80]; // File Managerプライベート変数
};
上記のように、FSSpec構造体でのファイル名格納場所はパスカル文字列で63バイト分しかありませんので、Unicodeで255文字(510バイト)のファイル名に対応できないのは明らかです。このままだと、Navigation Serviceで得られたロングファイル名のFSSpecは利用不可のように思われますが、何と「A long long long long file#23A4」といった具合に、名称の最後に「謎の参照値」(笑)が付加されて機能を維持しています。片やFSRef構造体の方は、内容が明示されていない(内緒の…)80バイトのデータ配列を持つだけです。こちらも構造体内にはロングファイル名は格納できないので、配列内に保存された何らかのリファレンス値で別構造体を参照し、ファイルを特定していると考えられます。
前回も言及しましたが、FSSpecは参照先ファイルが無くても定義できますが、FSRefの方はファイルが存在しないと定義できません。つまり、FSCreateFileUnicode() APIなどでファイルを作成する場合には、そのファイルが入るフォルダのFSRefと、Unicodeでエンコードされたファイル名が必要となるわけです。加えて、その構造体内だけではファイルへのリファレンス情報を保持できないので、定義したFSRef構造体をドキュメントに記録し、それを再度読み込んで同じファイルへアクセスしようとすると失敗します。つまりFSRef構造体は、その名前のとおりアプリケーション内で一時的に用いられる「ファイルへのリファレンス」として働いているわけです。
こうした理由から、本サンプルがドキュメント内に記録しているのは、再度読み込んでもファイルアクセスが再現できるFSSpec構造体の方です。もし、Unicodeのロングファイル名が必要になれば、FSSpecからFSRefを作り入手できます(今回のリスト表示では行っていませんが…)。また、FSRefをCFURLRefに変換してから記録しておくという手段もあります。CFURLRefは指定ファイルまでの「パス」をUTF8エンコードとして保存していますので、これだけで完全にファイル位置を把握可能です。加えて、CoreFoundation Frameworkには、ファイル参照のためにCFURLRefしか受け取らない「わがままなAPI」もありますので(笑)、それらを手なずけるのには都合が良いでしょう。ちなみにパスを記憶するだけならば、FSRefMakePath()でUTF8エンコードのパスを作成するという手段もあります。
ただし、FSSpecもCFURLRefもターゲットファイルの保存場所が変わる(パスが変更される)と、その時点で参照できなくなる欠点があります。そうしたケースが想定される場合には、FSSPecやFSRefからAliasHandleを作り、こちらをドキュメントへと記憶することで対処します。Alias Managerは受け取ったAliasHandleの中身を解析し、そのファイルを探し出すために「最大限の努力」をします。AliasHandle内にはファイルのID番号も記録されていますので、同じボリューム内ならば、保存場所が変えられても発見することができます。加えてネットワークボリュームのマウントを要求し、その中をサーチすることまで可能となっています。こうしたファイルの検索順序や方法については、Alias Managerのオプション設定により、ユーザ側で色々とコントロールすることが可能です。
続いて、FSRefを活用する例題として、ファイル名入力用のシートウィンドウを表示するnavPutFileSheet()ルーチンを紹介します。シートウィンドウは親ウィンドウ(通常は保存するドキュメントが表示されているウィンドウ)のタイトルバーの下から降りてきますので、ルーチンの引数には親ウィンドウのWindowRefを渡すことが必要です。
#define MY_SIG 'MosA' // アプリケーションのシグネイチャ
#define MY_DOC 'MosD' // ドキュメントのファイルタイプ
short navPutFileSheet( WindowRef window,Str255 title )
{
short ret=1;
NavDialogRef dptr;
NavDialogCreationOptions opt;
Str255 str;
if( ! NavGetDefaultDialogCreationOptions( &opt ) )
// ダイアログのオプションを初期化
{
opt.parentWindow=window; // シートの親ウィンドウを代入
opt.preferenceKey=MY_SIG; // 使用形態を識別するための値を設定
opt.optionFlags+=kNavNoTypePopup; // 種類ポップアップメニューを外す
opt.modality=kWindowModalityWindowModal; // シートであることを設定
GetIndString( str,128,1 );
opt.clientName=CFStringCreateWithPascalString( NULL,str,
CFStringGetSystemEncoding() );
// クライアント名をリソースから設定
opt.saveFileName=CFStringCreateWithPascalString( NULL,title,
CFStringGetSystemEncoding() );
// ディフォルトファイル名を設定する
if( ! NavCreatePutFileDialog( &opt,MY_DOC,MY_SIG,navPutEventSheetProc,
(void *)window,&dptr ) )
// シートウィンドウを作成する
{
if( ret=NavDialogRun( dptr ) ) // シートウィンドウを表示する
NavDialogDispose( dptr );
}
if( opt.saveFileName )
CFRelease( opt.saveFileName ); // ファイル名をリリース
if( opt.clientName )
CFRelease( opt.clientName ); // クライアント名をリリース
}
return( ret );
}
オプション設定の方法については、前回のNavPutFile()とあまり変わりません。ただし、NavGetDefaultDialogOptions()の代わりにNavGetDefaultDialogCreationOptions()を用い、文字列としてパスカルストリングスの代わりにCFStringRef(Unicode文字列)を代入します。
NavCreatePutFileDialog()でシートウィンドウを作成し、NavDialogRun()で表示させます。navMyPutFile()との違いは、シートウィンドウを表示し終わると、そのままルーチンから抜け出てしまうことです。では、保存ボタンなどが押された時の処理は、どこに記述すれば良いのでしょうか?
ユーザ操作に対応するための処理は、NavCreatePutFileDialog()に引数として渡されるイベント処理ルーチンのnavPutEventSheetProc()に記述します。
pascal void navPutEventSheetProc( NavEventCallbackMessage sel,
NavCBRecPtr parm,NavCallBackUserData ud )
{
NavReplyRecord reply;
FSRef fsref;
WindowRef wptr;
DescType type;
AEKeyword key;
Size len;
if( parm->context )
{
wptr=(WindowRef)ud; // 親ウィンドウのWindowRefを得る
switch( sel )
{
case kNavCBUserAction: // ユーザが何か操作を実行した
switch( parm->userAction )
{
case kNavUserActionSaveAs: // 保存ボタンが押された
if( ! NavDialogGetReply( parm->context,&reply ) )
// NavReplyRecord構造体を得る
{
AEGetNthPtr( &reply->selection,1,typeFSRef,
&key,&type,(Ptr)&fsref,sizeof( FSRef ),&len );
// ファイルを保存するフォルダのFSRefを得る
myDocumentSave( wptr,fsref,reply->saveFileName );
// ドキュメント保存用の自作ルーチン
// ファイルを保存するフォルダのfsrefと
// ファイル名のreply->saveFileNameを使い
// ドキュメントファイルの保存を実行する
NavDisposeReply( &reply );
// NavReplyRecord構造体を破棄
}
break;
case kNavUserActionCancel: // キャンセルボタンが押された
break;
}
break;
case kNavCBTerminate: // 作業が終了
NavDialogDispose( parm->context ); //シートウィンドウを削除
break;
}
}
}
ユーザがシートウィンドウ上でどんな操作をしたかは、引数で渡されるNavCBRecPtr経由で判断することが出来ます。この例では、保存ボタンがクリックされた時点でドキュメントを保存するフォルダのFSRef(ドキュメント自身のFSRefではないので注意!)を得て、それを入力ファイル名のreply->saveFileName(CFStringRef)と共に自作ルーチンに渡し、ドキュメントファイルを保存する処理を実行しています。
FSRefとCFURLRefの使用方法については以下の2つのテックノートが大変参考になりますので、興味がある方はぜひ参照してみてください。どちらも日本語訳が存在しています。
TN2078「Migrating to FSRefs & long Unicode names from FSSpecs」(日本語版)
http://developer.apple.com/ja/technotes/tn2078.html
TN2022「The Death of typeFSSpec:moving along to typeFileURL」(日本語版)
http://developer.apple.com/ja/technotes/tn2022.html
次回は、「フォルダ選択」や「ファイル読み込み」時に使うNavigation ServiceのAPIを説明します。具体的には、ファイルやフォルダを選択するためのダイアログを表示するNavChooseFolder()とNavGetFile()の使用方法や注意点についての解説となります。
つづく
「Behind the WebObjects」 第36回 田畑 英和
今回も前回に引き続き、これからWebObjectsを始めるデベロッパー向けの情報をまとめてみました。WOの情報が少ないというのはよく聞く話です。たしかに雑誌などで取り上げられることはあまりありませんが、それでも探してみると情報は色々あるものです。
まずはなんといってもAppleのドキュメントがあります。最新版の全てのドキュメントが日本語訳されてはいませんが、基本的なドキュメントは日本語版も用意されています。
・英語
http://developer.apple.com/referencelibrary/WebObjects/index.html
・日本語
http://www.apple.com/jp/webobjects/wo_docs_j.html
◇Getting Started
英語のほうのサイトの情報に基づいて解説を続けて行きますが、こちらのドキュメントを初めて見る方はまず上記URLの”Getting Started”を参照してください。ここでは目的別に各ドキュメントの簡単な説明があり、これから開発をおこなうデベロッパーはまず”WebObjects Overview”を読んで概要を把握するところから始めましょう。
◇WebObjects Overview
このドキュメントは日本語訳もあり、英語版では2002/1にWO5.1向けに改訂されたものをもとに翻訳されています。その後、英語版ではWO5.2で加わったWeb Servicesの記述が追加されるなどしていますが、WOの基本的な概要を理解するには日本語版でも十分でしょう。
◇Web Applications/Discovering WebObjects for HTML
HTMLベースのアプリケーションを開発する場合、次に読むドキュメントとして”Web Applications”が紹介されています。ここではアプリケーションの開発手順がサンプルをまじえて解説されており、サンプルのプロジェクトもWebからダウンロードすることができます。
このドキュメントは、もともと”Discovering WebObjects for HTML”として公開されていたものが、WO5.2のリリース時に改訂されてタイトルが現在のものに変更されています。また、そのさいバックトラッキングとキャッシュ管理に関する章が追加されています。一方、日本語版のほうは最初のバージョンに対応した翻訳ですので、タイトルも元にままになっており、注意が必要です。その他のドキュメントとしては、Direct To Webの解説や、データベースアクセス関連の、EOModelerの使い方やEnterprise Objectsの解説があります。
◇API Reference
API ReferenceとしてはJavaDoc形式のものがWeb上で参照可能であり、tgz形式にアーカイブされたものをダウンロードすることもできます。また、各バージョン間でのAPIの差分情報も5.1と5.2、5.2と5.2.2、5.2と5.2.3の組み合わせが用意されています。
現時点でのWebObjectsの最新版は5.2.3ですがGetting Startedページからのリンクは5.2.2のままになっていますのでご注意ください。
こういったドキュメントは開発時に頻繁に参照するため、できることなら日本語で用意されていたほうが便利でしょうが、残念ながら英語でしか提供されていません。
・WO5.2.3 Java Doc
http://developer.apple.com/documentation/WebObjects/Reference/API/index.html
◇その他の開発用ドキュメント
WebObjectsではHTMLベース以外のアプリケーションも開発することができますが、Web Servicesのドキュメントや、デスクトップアプリケーションを開発するためのJava Clientのドキュメントも用意されています。また、J2EE関連では、EJB、JSP、Servletsに関するドキュメントも用意されています。
◇運用ドキュメント
運用のためのドキュメントとしては”Deploying Applications”が用意されており、運用環境の解説から、具体的な運用時の操作方法まで詳しく解説されています。しかしAPI Referenceと同様にこちらも英語の情報のみとなります。
このようにまだいくつかのドキュメントは日本語訳されていないものもありますが、日本語独自の情報として、次のURLで開発と運用に関するものが用意されています。
・WebObjectsによる開発と運用についての技術情報
http://www.apple.com/jp/webobjects/techinfo/index.html
WebObjectsをインストールした場合も、英語版だけではありますが今回紹介したドキュメント一式がインストールされます。ただし、ドキュメントは追加や改訂がおこなわれる場合がありますので、最新版についてはWeb上のドキュメントを確認するようにしてください。
また、サンプルコードもWebObjectsに付属のものや、Webからダウンロードできるものがあります。
・WebObjects付属のドキュメント
/Developer/Documentation/WebObjects
・WebObjects付属のサンプル
/Developer/Examples/JavaWebObjects
・サンプルコード
http://www.apple.com/jp/webobjects/samples.html
今回紹介したドキュメントおよびサンプルだけでもかなりの量になりますが、ディスカッションの場としてAppleとOmni社がメーリングリストを提供しています。どちらも英語のメーリングリストになり、Appleが提供しているものは、開発、運用、アナウンスと3つに別れています。アナウンスはリリースなどに関してAppleからの発表に使われているものです。もっとも最近はアナウンスがあまりありませんが(T_T)
・Apple
http://lists.apple.com/mailman/listinfo/webobjects-dev
http://lists.apple.com/mailman/listinfo/webobjects-deploy
http://lists.apple.com/mailman/listinfo/webobjects-announce
・Omni
http://www.omnigroup.com/developer/mailinglists/
またまた英語での情報になりますが、入門用の書籍を1冊ご紹介しておきます。Mac OS X 10.2.2 + Project Builderをもとに執筆された書籍ですが、操作手順がスクリーンショット付きで丁寧に解説されており、内容も基本的な部分から運用までまんべんなくカバーされています。
「WebObjects 5 for Mac OS X(Visual Quickpro Guide)」
http://www.peachpit.com/title/032111549X
http://www.amazon.co.jp/exec/obidos/ASIN/032111549X
さて、英語での情報に少々かたよってしまいましたが、WebObjetsに関する情報をまとめてみました。もちろんWeb上ではこの他にも様々な情報にアクセスすることができますので、Googleを有効利用するなどして開発に役立ててください。
ニュース・解説
今週の解説担当:木下 誠
MOSADeNの読者の方、初めまして。HAPPY Macintosh Developing TIME! の木下といいます。今回から、新居さんの後を受けて、ニュース・解説を担当させていただくことになりました。CocoaとJava関連を中心に、Mac OS Xの開発関連のニュースをピックアップしていこうと思います。よろしくお願いします。
———————————————————————-
.Mac SDKが公開
———————————————————————-
Appleが、ADCで.Mac SDK 1.1を公開、およびドキュメント『Using the .Mac SDK』を公開していました。.Mac SDKは、2004年のWWDCで紹介されたフレームワークで、iDiskへのアクセス、.Macアカウントの管理などが行えます。必要となるシステムは、Mac OS X 10.2.6以降です。
.Mac SDKでは、Cocoa APIから使えるDotMacKit.frameworkを提供しています。このフレームワークを使うことで、自分のアプリケーションから、iAppと同じようにiDiskへのアクセスが行えます。また、SDKにはiDiskのブラウズや、iDiskへのバックアップを行うサンプルが含まれます。
Using the .Mac SDK
http://developer.apple.com/internet/dotmackit.html
———————————————————————-
テクニカルQA: Core AudioとXcode
———————————————————————-
AppleのTechnical Q&Aで、『QA1386: Core Audio & Xcode – Ensuring the latest Core Audio Headers are installed』が公開されていました。Xcode 1.5をインストールすると、Core Audioのヘッダが書き換えられてしまう問題を説明しています。
これは、QuickTime 6.5.2をインストールすると、Core Audioのヘッダとコンポーネントがアップデートされるために起こるようです。この場合、Xcodeのインストール時にMac OS X SDKのインストールをスキップすることが推奨されています。
QA1386: Core Audio & Xcode – Ensuring the latest Core Audio Headers are installed
http://developer.apple.com/qa/qa2004/qa1386.html
———————————————————————-
Dashboard開発サイトがオープン
———————————————————————-
Dashboardの開発情報を取り扱うWebサイト、DashboardDeveloperがオープンしていました。このサイトには、Dashboardのニュースを取り扱うほか、作成したWidgetを登録するギャラリーや、開発情報を議論するフォーラムがあります。Tigerはまだベータリリースもされていませんが、すでにWidgetの登録が行われているようです。
Tigerの目玉機能の1つとなるDashboardですが、完全に新しい環境なので、開発のノウハウはこれから作っていくことになると思います。こちらのフォーラムでは早速議論が行われているようなので、のぞいてみると面白いと思います。
Dashboard Developer
http://www.dashboarddev.com/
———————————————————————-
Xcodeを組み込み開発で利用
———————————————————————-
MacDevCenter.comで、Xcodeを組み込み開発に利用する記事、『Embedded Development with Xcode』が公開されていました。これは、XcodeのGUIを使って、リッチな環境で組み込み開発を行う方法を解説したものです。
記事では、ATMEL AVRチップを例題にして、Xcodeで組み込み開発環境を構築する手順を説明しています。Xcodeで、AVRチップ用のコンパイラ、アセンブラ、リンカといったツールを扱えるように設定するのがメインとなります。
Embedded Development with Xcode
http://www.macdevcenter.com/pub/a/mac/2005/01/21/embedded.html?CMP=ILC-macrss&ATT=Embedded+Development+with+Xcode
執筆者プロフィール:木下誠
HAPPY Macintosh Developing TIME!という、Mac開発系サイトを運営しています。Cocoaを中心に、Mac OS Xの開発の話題を取り扱っています。また、去年よりWeb Kitを使ってブラウザを作る、シイラプロジェクトを開始しています。最近はMac OS Xらしく、直感的で爽快なGUIを目指して、新たな動きを起こしています。
http://homepage.mac.com/mkino2/
MOSAからのお知らせと編集後記は割愛します