AS3 Dictionaryクラス

前に昼ご飯を食べながら話題になったAS3のDictionaryクラス。
自分なりに理解した過程。


akihiro kamijo: Dictionary クラス

オブジェクトをキーとして値を管理することができます。Object を使っても同じように連想配列を実現できますが、Dictionary ではいわゆる”弱い参照”が使えます。
また、Object では文字列がキーとして扱われるため toString() の値が同じオブジェクトは全て同じキーとして扱われますが、Dictionary では別々のオブジェクトであれば toString() の値に関わらず異なるキーとして扱われます。

「弱い参照」というのも一つのキーワード

弱い参照の使用

弱い参照とは、たとえ参照があってもガーベッジコレクションの対象となる種類の参照です。キャッシュなど”とりあえず置いておく”タイプのオブジェクトを管理するときなどに便利です。

Dictionary クラスは弱い参照を利用した weak-key と呼ばれるタイプのディクショナリーとして使うことができます。weak-key ディクショナリーでは、キーとして使われているオブジェクトへの参照が弱い参照になっています。そのため、キーオブジェクトへの参照が Dictionary からのみだと、ガーベッジコレクションのタイミングでキーオブジェクトが削除され、併せて Dictionary 内の該当するエントリが削除されます。

Dictionary クラスを weak-key ディクショナリーとして使うには、コンストラクタの引数に true を指定します。デフォルトは false になっています。

自分のことばで解釈。

連想配列を扱うとき、そのキーとなるオブジェクトへの参照の強さが
Object では強い(弱くない?)参照、Dictionary では弱い参照になる。

キーとなっているオブジェクトへの参照が他に存在しない時(弱い参照だけの時)、
そのオブジェクトはガーベッジコレクションのタイミングで削除される。
その時、同時にDictionary内の要素も削除される。

有効な使いどころを考えながら生活してみます。

– 参考サイト
akihiro kamijo: Dictionary クラス
akihiro kamijo: Dictionary クラス(前の続き)
Dictionary – ActionScript 3.0 コンポーネントリファレンスガイド

ReferenceError: Error #1065: 変数 [クラス名] は定義されていません。

ライブラリ内にムービークリップ「TestSp」をつくる。
クラス: TestSp
基本クラス: flash.display.Sprite

クラスの定義ファイル TestSp.as で 頭の class に public をつけ忘れたら
「ReferenceError: Error #1065: 変数 [クラス名] は定義されていません」
で止まった。理由は以下。

Flash CS3 ヘルプ

クラス、変数、定数、またはメソッドにすべての呼び出し元からアクセスできるよう指定します。クラス、変数、およびメソッドはデフォルトでは内部クラスです。つまり、現在のパッケージ内にのみ表示されます。クラス、変数、またはメソッドにすべての呼び出し元からアクセスできるようにするには、public 属性を使用する必要があります。

DisplayObject サブクラスの選択

普段、クラスに変数 mc を持たせ、後から MovieCLip への参照を代入する
FLASH OOP流で実装していたのだが、これからは Sprite を使いたい。

ステージ上に配置された MovieCLip を Sprite として扱うには
ライブラリのリンケージ機能から クラスと基本クラスをまとめて定義が必要。
そのときにクラス名も書かなければいけないが、Sprite を extends したクラスはつくらず、
「このクラスの定義がクラスパス内に見つからなかったため、
定義は書き出し時にSWFファイル内に自動生成されます」 に任せる。

さらに、ステージ上のインスタンス名とクラス名がかぶると当然エラーが出るので
「_class」とか付け足すとトラブルがないかもしれない。

というのが現在の方針。あとは作りながら検証。

Spriteにしておくと、仮に2フレーム名以降が存在しても
完全に無視されて、動作しない。

ムービークリップの基礎
DisplayObject サブクラスの選択

# 外部 SWF ファイルやイメージファイルをロードするためのコンテナが必要な場合、Loader インスタンスを使用します。ロードされたコンテンツは、Loader インスタンスの子として表示リストに追加されます。そのデータ型は、ロードされたコンテンツの特性によって次のように異なります。

* ロードされたイメージは Bitmap インスタンスになります。
* ActionScript 3.0 で記述された SWF ファイルをロードした場合、Sprite または MovieClip インスタンス (または、コンテンツ作成者の指定により、これらのクラスのサブクラスのインスタンス) になります。
* ActionScript 1.0 または ActionScript 2.0 で記述された SWF ファイルをロードした場合、AVM1Movie インスタンスになります。

# 他の表示オブジェクトのコンテナとなるオブジェクトが必要な場合 (ActionScript を使用して表示オブジェクトに描画するかどうかに関わらず)、次のいずれかの DisplayObjectContainer サブクラスを選択します。

* ActionScript のみを使用してオブジェクトを作成する場合、または ActionScript のみによって作成および操作されるカスタム表示オブジェクトの基本クラスとしては、Sprite を選択します。
* Flash オーサリングツールで作成されたムービークリップシンボルを参照するための変数を作成する場合、MovieClip を選択します。

# Flash ライブラリ内のムービークリップシンボルと関連付けられるクラスを作成する場合は、クラスの基本クラスとして、次のいずれかの DisplayObjectContainer サブクラスを選択します。

* 関連付けられたムービークリップシンボルのコンテンツが複数のフレーム上に存在する場合、MovieClip を選択します。
* 関連付けられたムービークリップシンボルのコンテンツが最初のフレームのみに存在する場合、Sprite を選択します。

try..catch..finally

AS2ではちょっとのエラーは気にせず進んでくれたが3では使うかも。
ランタイムエラーについて勉強。
try して throw されたエラーを catch できれば止まらない。

try..catch..finally ステートメント

エラーが発生する可能性のあるコードブロックを囲み、そのエラーに対処します。try..catch..finally ステートメントを使用して実装される例外処理は、ActionScript 3.0 でランタイムエラーの条件処理に使用される主要なメカニズムです。ランタイムエラーが発生すると、Flash Player は例外をスローして、通常の実行を一時停止し、Error 型の特殊なオブジェクトを作成します。次に、最初にアクセス可能な catch ブロックにエラーオブジェクトをパス (スロー) します。アクセス可能な catch ブロックがない場合、例外は不明な例外と見なされます。不明な例外が発生すると、スクリプトは終了します。

interface implements

interface と implements
まだまだ知らないことがたくさんある。

Flash CS3 ヘルプ

インターフェイスを定義します。インターフェイスとは、メソッドのセットを定義するデータ型です。このメソッドは、インターフェイスを実装するすべてのクラスで定義される必要があります。

インターフェイスはクラスに似ていますが、次に示す重要な違いがあります。

・インターフェイスにはメソッドの宣言だけが含まれます。メソッド実装は含まれません。つまり、インターフェイスを実装するすべてのクラスは、インターフェイスで宣言されている各メソッドの実装を定義する必要があります。
・インターフェイスメソッドの定義では、public または private などの属性を指定できませんが、実装されたメソッドはインターフェイスを実装するクラスの定義で public と指定する必要があります。
・extends ステートメントまたは implements ステートメントを介したクラスを使用して、インターフェイスで複数のインターフェイスを継承することができます。
・ActionScript 2.0 とは異なり、ActionScript 3.0 ではインターフェイス定義で getter および setter メソッドを使用できます。

TestInterface.as

/* ActionScript3 */

package{
public interface TestInterface{
function func1():void;
}
}

Init.as

/* ActionScript3 */

package{

import flash.display.Sprite;

public class Init implements TestInterface{

public function Init(sp:Sprite){
}

public function func1():void{
trace("func1");
}

}
}

Init 内で func1を書かないと
「1044: インターフェイスメソッド func1 (名前空間 TestInterface) はクラス Init によって実装されません。」
というエラーが出る。

- 参考サイト
Flash ActionScript入門ノート: 4.5 インターフェイス

const

const の命名は 「大文字だけを使い、単語は_で区切る」
値は宣言時にしか定義できない。

変数 — Flex 2.01

var キーワードで宣言した変数は、スクリプトの実行中に複数回その値を変更できます。const キーワードで宣言した変数は定数と呼ばれ、1 回だけ値を割り当てることができます。初期化された定数に新しい値を割り当てようとすると、エラーが発生します。

/* ActionScript3 */

package{

public class Init{

private const NUM:int;

public function Init(){
this.NUM = 100; //結果: 1049: 定数として指定した変数への代入が無効です。
}

}
}

- 参考サイト
2.2 変数と定数 (Flash ActionScript3.0入門ノート)

arguments.caller

[object global]

AS2でおなじみの手法は
AS3で「望ましくないイベントリスナー登録の方法」になったらしい。
バインドメソッドメソッドクロージャの理解はやはり重要。

イベントリスナー — Flex 2.01 望ましくないイベントリスナー登録の方法

第 3 のテクニックとして、汎用オブジェクトを作成し、そのオブジェクトのプロパティによって動的にリスナー関数を割り当てる方法がありますが、これを使用することはお勧めできません。この方法を説明するのは、ActionScript 2.0 で一般的に使用されていたという理由からです。ActionScript 3.0 では使用しないでください。このテクニックでは this キーワードの参照先がリスナーオブジェクトではなくグローバルオブジェクトになるため、望ましくありません。

[object global] というのが、参照先を見失ってしまっている状態?

/* ActionScript3 */

import flash.events.MouseEvent;

var testData1:String = "TEST1";

var myListenerObj:Object = new Object();
myListenerObj.testData2 = "TEST2";
myListenerObj.onMouseUpFunc1 = function (e:MouseEvent):void
{
trace("event.type: " + e.type); //結果: event.type: mouseDown
trace("this: " + this); //結果: this: [object global]
trace("this.testData1: " + this.testData1); //結果: this.testData1: undefined
trace("this.testData2: " + this.testData2); //結果: this.testData2: undefined
};

function onMouseUpFunc2 (e:MouseEvent):void {
trace ("event.type: " + e.type); //結果: event.type: mouseUp
trace ("this: " + this); //結果: this: [object MainTimeline]
trace ("this.testData1: " + this.testData1); //結果: this.testData1: TEST1
}

stage.addEventListener (MouseEvent.MOUSE_DOWN, myListenerObj.onMouseUpFunc1);
stage.addEventListener (MouseEvent.MOUSE_UP, this.onMouseUpFunc2);

this の参照先は myListenerObj であるかのように見え、トレース出力も [object Object]
になっていますが
(なっていない…)、実際の参照先はグローバルオブジェクトです。動的なプロパティ名を addEventListener() のパラメータとして指定した場合、Flash Player はバインドメソッドを作成できません。なぜなら、その場合に listener パラメータとして渡されるのはリスナー関数の単なるメモリアドレスであり、Flash Player がそのメモリアドレスと myListenerObj インスタンスを結び付ける手段がないからです。

more >

Function.apply

Delegateメソッドクロージャと関連して、Function.apply。
function test1():void{ } で定義したメソッドは apply が効かない。

/* ActionScript3 */

function test1():void{
trace(this);
}

var test2:Function = function():void{
trace(this);
}

var obj:Object = new Object();
this.test1.apply(obj); //結果: [object MainTimeline]
this.test2.apply(obj); //結果: [object Object]

“this 参照が、常にこのメソッドを実装する元のオブジェクトを指している”
が test1 (バインドメソッド)
“this 参照は汎用的で、呼び出されたときにこの関数が関連付けられているオブジェクトを指”す
が test2 (メソッドクロージャ)

メソッド — Flex 2.01

バインドメソッドは、メソッドクロージャとも呼ばれる、単にインスタンスから抽出されるメソッドです。バインドメソッドの例には、関数にパラメータとして渡され、関数から値として返されるメソッドがあります。ActionScript 3.0 で新しく導入されたバインドメソッドは、インスタンスから抽出されたときでもレキシカル環境が保持されるメソッドクロージャに似ています。バインドメソッドとメソッドクロージャの主な違いは、バインドメソッドの this 参照は、バインドメソッドを実装するインスタンスにリンクされたまま、つまりバインドされたままであるという点です。これは、バインドメソッドの this 参照が、常にこのメソッドを実装する元のオブジェクトを指していることを意味します。メソッドクロージャの場合、this 参照は汎用的で、呼び出されたときにこの関数が関連付けられているオブジェクトを指します。

more >

メソッドクロージャ

AS3の「メソッドクロージャ」が実はとても役立つことがわかった。

関数スコープ — Flex 2

メソッドクロージャ
ActionScript 3.0 では、元のオブジェクトインスタンスを自動的に記憶するメソッドクロージャが有効です。この機能はイベント処理に役立ちます。ActionScript 2.0 では、メソッドクロージャに抽出元のオブジェクトインスタンスが記憶されず、メソッドクロージャが呼び出されたときに予期しない動作が発生しました。mx.utils.Delegate クラスは一般的な方法でしたが、不要になりました。

Flex2/ActionScript3.0/関数 – PukiWiki

* 関数をクラスの一部として定義した場合と、インスタンスに関連付けた場合はメソッドと呼ぶ。
* それ以外の方法で定義された場合はメソッドクロージャと呼ぶ

上から2つ目のtrace(this)の結果 “[object MainTimeline]” がメソッドクロージャの効果。

/* ActionScript3 */

Tweener.addTween(this._btnMc, {alpha: 0.5, time:1, onComplete: function():void{trace(this);}});
//結果: [object MovieClip] (addTweenで扱ったオブジェクト)

Tweener.addTween(this._btnMc, {alpha: 0.5, time:1, onComplete: this.onCompleteFunc});
private function onCompleteFunc():void{
trace(this);
}
//結果: [object MainTimeline] (addTween呼び出し元のthis)

AS2ではこうならなかった。

/* ActionScript2 */

Tweener.addTween(this._btnMc, {_alpha: 50, time:1, onComplete: function():Void{trace(this);}});
//結果: _level0.btn_mc (addTweenで扱ったオブジェクト)

Tweener.addTween(this._btnMc, {_alpha: 50, time:1, onComplete: this.onCompleteFunc});
private function onCompleteFunc():void{
trace(this);
}
//結果: _level0.btn_mc (addTweenで扱ったオブジェクト)

more >