タグ: AppleScriptObjC

  • AppleScriptObjC を Cocoa/Objective-C アプリに統合する

    Snow Leopard 登場後、いきなり超応用編からスタートしたこの AppleScriptObjC シリ ーズ(?)ですが、今回でひと区切りとさせていただきたいと思います。

    もっと基礎的な部分に関しては、本家が出す、と言っているサンプルコードやドキュメントにお任せたいところですが、あまり期待できないので、精力的にサンプルコードをアップしている MacScripter / unScripted などをご参照ください。あるいは、何かリクエストをください。

    最後は、Cocoa/Objective-C アプリで AppleScriptObjC を使えるようにする というお話。プロジェクト設定レベルのことなので、技術的な意義はあまりありませんが、よろしければご覧ください。

    1. 任意の Cocoa/Objective-C プロジェクトを用意する

    まずは、任意の Cocoa/Objective-C プロジェクトを用意します。これは既存のもので構いません。

    ここでは、あえて Core Data アプリ向けのテンプレートを使ってみます。

    2. AppleScriptObjC.framework をプロジェクトに追加する

    次に、AppleScriptObjC.framework をプロジェクトに追加します。やり方はいろいろありますが、ここでは「Linked Frameworks」項目の上での右クリックから追加しています。

    メニューを選択すると、フレームワーク選択シートが現れますので、AppleScriptObjC.framework を選択して「追加」ボタンを押せば OK です。

    3. ガーベジコレクションを有効にする

    Cocoa/Objective-C アプリのテンプレートでは、デフォルトでは ガーベジコレクション非対応 に設定されています。AppleScriptObjC を利用するには、どう設定するのが望ましいのでしょうか。

    otool -ov /System/Library/Frameworks/AppleScriptObjC.framework/Versions/A/AppleScriptObjC で AppleScriptObjC フレームワークの GC 対応状況を調べてみたところ、GC RR(ガーベジコレクションまたは Retain/Release 方式)となっており、AppleScriptObjC フレームワークは ガーベジコレクションサポートあり-fobjc-gc)でビルドされていることが分かります。

    …が、実際には、ガーベジコレクション非対応でアプリをビルドすると、ぼろぼろ警告が出てくる(おい)ので、ガーベジコレクションの設定は、サポートありまたは 必須-fobjc-gc-only)にするのが望ましいようです。ちなみに、AppleScriptObjC のテンプレートでは 必須がデフォルト となっています。

    そこで、ガーベジコレクションの設定を切り替えます。これもいろいろ方法がありますが、ここでは「ターゲット」グループ内のターゲットをダブルクリックして設定ウィンドウを表示しています。

    4. main 関数を編集する

    最後に、main 関数にスクリーンショット内でアスタリスク(*)のコメントを入れてある 2 行を追加します。2 行で行っていることは次の通り:

    1. AppleScriptObjC.h をインポート
    2. バンドル内のスクリプトを読み込む(ここでスクリプトから Objective-C クラスが生成されるのでしょうか)

    以上で、Cocoa/Objective-C ベースのプロジェクトでも、AppleScript で書いたクラスを活用できるようになります。サンプルコードでは、これまでの操作に加えて、実際に AppleScript で「Hello World」を表示するクラスを作成し、実際にボタンから表示させることができるようにしてあります。

    サンプルコード


    AppleScriptObjC は AppleScript Studio の後継と言われ、AppleScript だけでフルスペックの Cocoa アプリを開発できる環境として登場しましたが、Cocoa/Objective-C 開発者にも、自分のアプリにより簡単に AppleScript のアシストを受けられる手段として注目してほしい(Apple が意図した使い方ではないとは思いますが)、というのが、今回の三部作のテーマでした。

    AppleScriptObjC 以前に用意されていた NSAppleScript クラスや Scripting Bridge を使うと、処理がスクリプト単位になって取り回しが難しかったり、Objective-C で 「AppleScript っぽいもの」を書く、という地獄(少なくとも私にとっては)を味わうことになりますが、かと言って、AppleScriptObjC を使ってアプリ全編を AppleScript で書く、というのも、Cocoa/Objective-C 開発者にとっては現実的ではないと思います。

    これまでに紹介させていただいた方法を使って、AppleScript をうまく活用した面白い Cocoa アプリが生まれてくることを願っています(てか、Cocoa/Objective-C 開発者が対象なら、ここまで細かく設定方法を書く必要なかったな…汗)。

  • クラスメソッドかインスタンスメソッドか

    AppleScriptObjC に関しては、風のウワサで「何でもできるよ!」と聞いてはいたのですが、Cocoa/Objective-C と AppleScript の両方やっている身からすると、触っていくうちにそこかしこで「え? Cocoa のあれどうやってやるの?」という場面に遭遇します。

    ふと思ったのが、AppleScript のクラスでどうやってクラスメソッドを定義するのか ということです。ひとまずの結論としては、クラスメソッドとして呼びたければそのように、インスタンスメソッドとして呼びたければそのように呼べばいい ようです。

    サンプルコード

    ClassMethodOrInstanceMethodScreenSnapz001

    説明

    このサンプルコードでは、まず CMGhost というくらすを AppleScript で書いています。このクラスは、パラメータとして文字列を受け取り、標準アラート(NSAlert)でそれを表示する、という単純なハンドラ(メソッド)identifyWithMessage_() を持つのみの簡単なものです。

    ビルドして起動すると、3つのボタンがウィンドウに並んでいます。最初の「AppleScript」では、前述の identifyWithMessage_() を, 最初はクラスメソッドとして、その後インスタンスメソッドとして呼び出します。「Objective-C」も処理内容は同じですが、Objective-C ベースで呼び出しています。最後の「Dump Methods」ボタンでは、CMGhost クラスの持つメソッドを PPDumper クラスを用いてダンプしています。

    実行結果は、AppleScript でも Objective-C でも、ひとつのメソッドをクラス/インスタンス両方のメソッドとして呼び出せる、というものになると思います。

    その他

    このサンプルコードでは、以下の項目も参考になるかもしれません。

    AppleScriptObjC におけるクラスの参照の仕方

    ClassMethodOrInstanceMethodAppDelegate.applescript で確認することができます。

    AppleScriptObjC では、クラス(オブジェクト?)は current application の持ち物になっています。そのままだと非常に冗長になっていますので、サンプルコードのように、あらかじめトップレベルのプロパティとして持っておくと、多少すっきりするかもしれません。

    さらにサンプルコード内の _class() ハンドラの用なものを作ろうとする方がいらっしゃるかもしれませんが、それはできません。なぜなら、return された後はインスタンス化されてしまうからです(この辺は今回のテーマにも関連しそうですが、あまり深入りしないことにしました…おい)。

    可変引数を持ったメソッドの呼び出し

    今回 CMGhost クラスでは、テキストの表示に display dialog コマンドではなく、Cocoa のクラス NSAlert を使用しています。

    NSAlert にはインスタンスを生成するための長ったらしいクラスメソッドがあるのですが、これの最後で文字列フォーマットを指定できるようになっています。これはどうやるのかというと… そのまま です。ちなみに、missing valuenil です。

  • AppleScript で書いたクラスを ObjC から使う

    XcodeScreenSnapz011

    Snow Leopard では、AppleScript Studio の後を継いで AppleScriptObjC.framework が導入され、Cocoa の大半の機能を AppleScript から利用できるようになりました。

    例えば、AppleScript から Cocoa/Objective-C のクラスを利用したり、AppleScript でそれらのクラスのサブクラスを作成できるようになりました(言い換えれば、作成しなければならなくなった …笑)。

    少し残念なのは、前述の通り 「AppleScript から Cocoa の機能を利用する」ことに重点が置かれており、Cocoa/Objective-C の世界から AppleScript を利用する ことには(まだそんなに調べていませんが)重点が置かれていないようです。

    ただ、AppleScript で書いたクラスも、アプリケーションの動作中には Objective-C のクラスとして存在するわけで、それにアクセスするのは、(ヒネた内部構造になっていなければ)さほど難しくないはず…。

    というわけで作成したのが、今回のサンプルコードです。

    サンプルコード

    これは何?

    内容は、ボタンを押すとダイアログが表示される、という単純なものです。ただし、内部では Objective-C で書かれた MainController が AppleScript で書かれた Talker クラスをインスタンス化し、文字列の配列を設定し、最後に talk() メソッドを呼んでいます。talk() メソッドの中身は、文字列をループで取り出し、(往年の)display dialog コマンドで表示する、ほぼ Pure AppleScript なものになっています。

    これができると何がうれしいかというと、Cocoa/Objective-C アプリから AppleScript を呼び出すのがよりシンプルになる と思います。

    最近でこそ Scripting Bridge で少しはマシになったと思いますが、前準備が必要だったり、操作対象のアプリが決まっている必要があったりと、(あまり使ったことはないのですが)なかなか辛そうです。

    これからは、例えば AppleScript による処理を担当するクラスをひとつ作っておけば、好きなように AppleScript を呼び出すことができます。